Update release notes for 22.05.22 release
[koha.git] / t / db_dependent / StockRotationItems.t
1 #!/usr/bin/perl
2
3 # Copyright PTFS Europe 2016
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 DateTime;
23 use DateTime::Duration;
24 use Koha::Database;
25 use Koha::DateUtils qw( dt_from_string );
26 use Koha::Item::Transfer;
27
28 use Test::Warn;
29 use t::lib::TestBuilder;
30 use t::lib::Mocks;
31
32 use Test::More tests => 9;
33
34 my $schema = Koha::Database->new->schema;
35
36 use_ok('Koha::StockRotationItems');
37 use_ok('Koha::StockRotationItem');
38
39 my $builder = t::lib::TestBuilder->new;
40
41 subtest 'Basic object tests' => sub {
42
43     plan tests => 5;
44
45     $schema->storage->txn_begin;
46
47     my $itm = $builder->build_sample_item;
48     my $stage = $builder->build({ source => 'Stockrotationstage' });
49
50     my $item = $builder->build({
51         source => 'Stockrotationitem',
52         value  => {
53             itemnumber_id => $itm->itemnumber,
54             stage_id      => $stage->{stage_id},
55         },
56     });
57
58     my $sritem = Koha::StockRotationItems->find($item->{itemnumber_id});
59     isa_ok(
60         $sritem,
61         'Koha::StockRotationItem',
62         "Correctly create and load a stock rotation item."
63     );
64
65     # Relationship to rota
66     isa_ok( $sritem->item, 'Koha::Item', "Fetched related item." );
67     is( $sritem->item->itemnumber, $itm->itemnumber, "Related rota OK." );
68
69     # Relationship to stage
70     isa_ok( $sritem->stage, 'Koha::StockRotationStage', "Fetched related stage." );
71     is( $sritem->stage->stage_id, $stage->{stage_id}, "Related stage OK." );
72
73
74     $schema->storage->txn_rollback;
75 };
76
77 subtest 'Tests for needs_repatriating' => sub {
78
79     plan tests => 4;
80
81     $schema->storage->txn_begin;
82
83     # Setup a pristine stockrotation context.
84     my $sritem = $builder->build(
85         {
86             source => 'Stockrotationitem',
87             value =>
88               { itemnumber_id => $builder->build_sample_item->itemnumber }
89         }
90     );
91     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
92     $dbitem->item->homebranch($dbitem->stage->branchcode_id);
93     $dbitem->item->holdingbranch($dbitem->stage->branchcode_id);
94     $dbitem->stage->position(1);
95
96     my $dbrota = $dbitem->stage->rota;
97     my $newstage = $builder->build({
98         source => 'Stockrotationstage',
99         value => {
100             rota_id => $dbrota->rota_id,
101             position => 2,
102         }
103     });
104
105     # - homebranch == holdingbranch [0]
106     is(
107         $dbitem->needs_repatriating, 0,
108         "Homebranch == Holdingbranch."
109     );
110
111     my $branch = $builder->build({ source => 'Branch' });
112     $dbitem->item->holdingbranch($branch->{branchcode});
113
114     # - homebranch != holdingbranch [1]
115     is(
116         $dbitem->needs_repatriating, 1,
117         "Homebranch != holdingbranch."
118     );
119
120     # Set to incorrect homebranch.
121     $dbitem->item->holdingbranch($dbitem->stage->branchcode_id);
122     $dbitem->item->homebranch($branch->{branchcode});
123     # - homebranch != stockrotationstage.branch & not in transit [1]
124     is(
125         $dbitem->needs_repatriating, 1,
126         "Homebranch != StockRotationStage.Branchcode_id & not in transit."
127     );
128
129     # Set to in transit (by implication).
130     $dbitem->stage($newstage->{stage_id});
131     # - homebranch != stockrotaitonstage.branch & in transit [0]
132     is(
133         $dbitem->needs_repatriating, 1,
134         "homebranch != stockrotaitonstage.branch & in transit."
135     );
136
137     $schema->storage->txn_rollback;
138 };
139
140 subtest "Tests for repatriate." => sub {
141     plan tests => 9;
142     $schema->storage->txn_begin;
143
144     my $sritem_1 = $builder->build_object(
145         {
146             class => 'Koha::StockRotationItems',
147             value  => {
148                 itemnumber_id => $builder->build_sample_item->itemnumber
149             }
150         }
151     );
152     my $item_id = $sritem_1->item->itemnumber;
153     my $srstage_1 = $sritem_1->stage;
154     $sritem_1->discard_changes;
155     $sritem_1->stage->position(1);
156     $sritem_1->stage->duration(50);
157     my $branch = $builder->build({ source => 'Branch' });
158     $sritem_1->item->holdingbranch($branch->{branchcode});
159
160     # Test a straight up repatriate
161     ok($sritem_1->repatriate, "Repatriation done.");
162     my $intransfer = $sritem_1->item->get_transfer;
163     is($intransfer->frombranch, $branch->{branchcode}, "Origin correct.");
164     is($intransfer->tobranch, $sritem_1->stage->branchcode_id, "Target Correct.");
165
166     # Reset
167     $intransfer->datearrived(dt_from_string())->store;
168     $sritem_1->item->holdingbranch($branch->{branchcode});
169
170     # Setup a conflicting manual transfer
171     my $item = Koha::Items->find($item_id);
172     $item->request_transfer({ to => $srstage_1->branchcode, reason => "Manual" });
173     $intransfer = $item->get_transfer;
174     is (ref($intransfer), 'Koha::Item::Transfer', "Conflicting transfer added");
175     is ($intransfer->reason, 'Manual', "Conflicting transfer reason is 'Manual'");
176
177     # Stockrotation should handle transfer clashes
178     is($sritem_1->repatriate, 0, "Repatriation skipped if transfer in progress.");
179
180     # Reset
181     $intransfer->datearrived(dt_from_string())->store;
182     $sritem_1->item->holdingbranch($branch->{branchcode});
183
184     # Confirm that stockrotation ignores transfer limits
185     t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1);
186     t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype');
187     my $limit = Koha::Item::Transfer::Limit->new(
188         {
189             fromBranch => $branch->{branchcode},
190             toBranch   => $srstage_1->branchcode_id,
191             itemtype   => $sritem_1->item->effective_itemtype,
192         }
193     )->store;
194
195     # Stockrotation should overrule transfer limits
196     ok($sritem_1->repatriate, "Repatriation done regardless of transfer limits.");
197     $intransfer = $sritem_1->item->get_transfer;
198     is($intransfer->frombranch, $branch->{branchcode}, "Origin correct.");
199     is($intransfer->tobranch, $sritem_1->stage->branchcode_id, "Target Correct.");
200
201     $schema->storage->txn_rollback;
202 };
203
204 subtest "Tests for needs_advancing." => sub {
205     plan tests => 8;
206     $schema->storage->txn_begin;
207
208     # Test behaviour of item freshly added to rota.
209     my $sritem = $builder->build(
210         {
211             source => 'Stockrotationitem',
212             value  => {
213                 'fresh'       => 1,
214                 itemnumber_id => $builder->build_sample_item->itemnumber
215             },
216         }
217     );
218     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
219     is($dbitem->needs_advancing, 1, "An item that is fresh will always need advancing.");
220
221     # Setup a pristine stockrotation context.
222     $sritem = $builder->build(
223         {
224             source => 'Stockrotationitem',
225             value  => {
226                 'fresh'       => 0,
227                 itemnumber_id => $builder->build_sample_item->itemnumber
228             }
229         }
230     );
231     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
232     $dbitem->item->homebranch($dbitem->stage->branchcode_id);
233     $dbitem->item->holdingbranch($dbitem->stage->branchcode_id);
234     $dbitem->stage->position(1);
235     $dbitem->stage->duration(50);
236
237     my $dbtransfer = Koha::Item::Transfer->new({
238         'itemnumber'  => $dbitem->itemnumber_id,
239         'frombranch'  => $dbitem->stage->branchcode_id,
240         'tobranch'    => $dbitem->stage->branchcode_id,
241         'datesent'    => dt_from_string(),
242         'datearrived' => undef,
243         'reason'      => "StockrotationAdvance",
244     })->store;
245
246     # Test item will not be advanced if in transit.
247     is($dbitem->needs_advancing, 0, "Not ready to advance: in transfer.");
248     # Test item will not be advanced if in transit even if fresh.
249     $dbitem->fresh(1)->store;
250     is($dbitem->needs_advancing, 0, "Not ready to advance: in transfer (fresh).");
251     $dbitem->fresh(0)->store;
252
253     # Test item will not be advanced if it has not spent enough time.
254     $dbtransfer->datearrived(dt_from_string())->store;
255     is($dbitem->needs_advancing, 0, "Not ready to advance: Not spent enough time.");
256     # Test item will be advanced if it has not spent enough time, but is fresh.
257     $dbitem->fresh(1)->store;
258     is($dbitem->needs_advancing, 1, "Advance: Not spent enough time, but fresh.");
259     $dbitem->fresh(0)->store;
260
261     # Test item will be advanced if it has spent enough time.
262     $dbtransfer->datesent(      # Item was sent 100 days ago...
263         dt_from_string() - DateTime::Duration->new( days => 100 )
264     )->store;
265     $dbtransfer->datearrived(   # And arrived 75 days ago.
266         dt_from_string() - DateTime::Duration->new( days => 75 )
267     )->store;
268     is($dbitem->needs_advancing, 1, "Ready to be advanced.");
269
270     # Bug 30518: Confirm that DST boundaries do not explode.
271     # mock_config does not work here, because of tz vs timezone subroutines
272     my $context = Test::MockModule->new('C4::Context');
273     $context->mock( 'tz', sub {
274         'Europe/London';
275     });
276     my $bad_date = dt_from_string("2020-09-29T01:15:30", 'iso');
277     $dbtransfer->datesent($bad_date)->store;
278     $dbtransfer->datearrived($bad_date)->store;
279     $dbitem->stage->duration(180)->store;
280     is( $dbitem->needs_advancing, 1, "DST boundary doesn't cause failure." );
281     $context->unmock('tz');
282
283     # Test that missing historical branch transfers do not crash
284     $dbtransfer->delete;
285     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.";
286
287     $schema->storage->txn_rollback;
288 };
289
290 subtest "Tests for advance." => sub {
291     plan tests => 48;
292     $schema->storage->txn_begin;
293
294     my $sritem_1 = $builder->build_object(
295         {
296             class => 'Koha::StockRotationItems',
297             value  => {
298                 'fresh'       => 1,
299                 itemnumber_id => $builder->build_sample_item->itemnumber
300             }
301         }
302     );
303     $sritem_1->discard_changes;
304     $sritem_1->item->holdingbranch($sritem_1->stage->branchcode_id);
305     my $item_id = $sritem_1->item->itemnumber;
306     my $srstage_1 = $sritem_1->stage;
307     $srstage_1->position(1)->duration(50)->store; # Configure stage.
308     # Configure item
309     $sritem_1->item->holdingbranch($srstage_1->branchcode_id)->store;
310     $sritem_1->item->homebranch($srstage_1->branchcode_id)->store;
311     # Sanity check
312     is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Stage sanity check.");
313
314     # Test if an item is fresh, always move to first stage.
315     is($sritem_1->fresh, 1, "Fresh is correct.");
316     $sritem_1->advance;
317     is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Stage is first stage after fresh advance.");
318     is($sritem_1->fresh, 0, "Fresh reset after advance.");
319
320     # Test cases of single stage
321     $srstage_1->rota->cyclical(1)->store;         # Set Rota to cyclical.
322     ok($sritem_1->advance, "Single stage cyclical advance done.");
323     ## Refetch sritem_1
324     $sritem_1->discard_changes;
325     is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Single stage cyclical stage OK.");
326
327     # Test with indemand advance
328     $sritem_1->indemand(1)->store;
329     ok($sritem_1->advance, "Indemand item advance done.");
330     ## Refetch sritem_1
331     $sritem_1->discard_changes;
332     is($sritem_1->indemand, 0, "Indemand OK.");
333     is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Indemand item advance stage OK.");
334
335     # Multi stages
336     my $srstage_2 = $builder->build_object({
337         class => 'Koha::StockRotationStages',
338         value => { duration => 50 }
339     });
340     $srstage_2->discard_changes;
341     $srstage_2->move_to_group($sritem_1->stage->rota_id);
342     $srstage_2->move_last;
343
344     # Test a straight up advance
345     ok($sritem_1->advance, "Advancement done.");
346     ## Refetch sritem_1
347     $sritem_1->discard_changes;
348     ## Test results
349     is($sritem_1->stage->stage_id, $srstage_2->stage_id, "Stage updated.");
350     is(
351         $sritem_1->item->homebranch,
352         $srstage_2->branchcode_id,
353         "Item homebranch updated"
354     );
355     my $transfer_request = $sritem_1->item->get_transfer;
356     is($transfer_request->frombranch, $srstage_1->branchcode_id, "Origin correct.");
357     is($transfer_request->tobranch, $srstage_2->branchcode_id, "Target Correct.");
358     is($transfer_request->datesent, undef, "Transfer requested, but not sent.");
359
360     # Arrive at new branch
361     $transfer_request->datearrived(dt_from_string())->store;
362     $sritem_1->item->holdingbranch($srstage_2->branchcode_id)->store;
363
364     # Test a cyclical advance
365     ok($sritem_1->advance, "Cyclical advancement done.");
366     ## Refetch sritem_1
367     $sritem_1->discard_changes;
368     ## Test results
369     is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Stage updated.");
370     is(
371         $sritem_1->item->homebranch,
372         $srstage_1->branchcode_id,
373         "Item homebranch updated"
374     );
375     $transfer_request = $sritem_1->item->get_transfer;
376     is($transfer_request->frombranch, $srstage_2->branchcode_id, "Origin correct.");
377     is($transfer_request->tobranch, $srstage_1->branchcode_id, "Target correct.");
378
379     # Arrive at new branch
380     $transfer_request->datearrived(dt_from_string())->store;
381     $sritem_1->item->holdingbranch($srstage_1->branchcode_id)->store;
382
383     # Confirm that stockrotation ignores transfer limits
384     t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1);
385     t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype');
386     my $limit = Koha::Item::Transfer::Limit->new(
387         {
388             fromBranch => $srstage_1->branchcode_id,
389             toBranch   => $srstage_2->branchcode_id,
390             itemtype   => $sritem_1->item->effective_itemtype,
391         }
392     )->store;
393
394     ok($sritem_1->advance, "Advancement overrules transfer limits.");
395     ## Refetch sritem_1
396     $sritem_1->discard_changes;
397     ## Test results
398     is($sritem_1->stage->stage_id, $srstage_2->stage_id, "Stage updated ignoring transfer limits.");
399     is(
400         $sritem_1->item->homebranch,
401         $srstage_2->branchcode_id,
402         "Item homebranch updated ignoring transfer limits"
403     );
404     $transfer_request = $sritem_1->item->get_transfer;
405     is($transfer_request->frombranch, $srstage_1->branchcode_id, "Origin correct ignoring transfer limits.");
406     is($transfer_request->tobranch, $srstage_2->branchcode_id, "Target correct ignoring transfer limits.");
407
408     # Arrive at new branch
409     $transfer_request->datearrived(dt_from_string())->store;
410     $sritem_1->item->holdingbranch($srstage_2->branchcode_id)->store;
411
412     # Setup a conflicting manual transfer
413     my $item = Koha::Items->find($item_id);
414     $item->request_transfer({ to => $srstage_1->branchcode, reason => "Manual" });
415     $transfer_request = $item->get_transfer;
416     is (ref($transfer_request), 'Koha::Item::Transfer', "Conflicting transfer added");
417     is ($transfer_request->reason, 'Manual', "Conflicting transfer reason is 'Manual'");
418
419     # Advance item whilst conflicting manual transfer exists
420     ok($sritem_1->advance, "Advancement done.");
421     ## Refetch sritem_1
422     $sritem_1->discard_changes;
423
424     ## Refetch conflicted transfer
425     $transfer_request->discard_changes;
426
427     # Conflicted transfer should have been cancelled
428     isnt($transfer_request->datecancelled, undef, "Conflicting manual transfer was cancelled");
429
430     # StockRotationAdvance transfer added
431     $transfer_request = $sritem_1->item->get_transfer;
432     is($transfer_request->reason, 'StockrotationAdvance', "StockrotationAdvance transfer added");
433     is($transfer_request->frombranch, $srstage_2->branchcode_id, "Origin correct.");
434     is($transfer_request->tobranch, $srstage_1->branchcode_id, "Target correct.");
435
436     # Arrive at new branch
437     $transfer_request->datearrived(dt_from_string())->store;
438     $sritem_1->item->holdingbranch($srstage_1->branchcode_id)->store;
439
440     # Setup a conflicting reserve transfer
441     $item->request_transfer({ to => $srstage_2->branchcode, reason => "Reserve" });
442     $transfer_request = $item->get_transfer;
443     is (ref($transfer_request), 'Koha::Item::Transfer', "Conflicting transfer added");
444     is ($transfer_request->reason, 'Reserve', "Conflicting transfer reason is 'Reserve'");
445
446     # Advance item whilst conflicting reserve transfer exists
447     ok($sritem_1->advance, "Advancement done.");
448     ## Refetch sritem_1
449     $sritem_1->discard_changes;
450
451     ## Refetch conflicted transfer
452     $transfer_request->discard_changes;
453
454     # Conflicted transfer should not been cancelled
455     is($transfer_request->datecancelled, undef, "Conflicting reserve transfer was not cancelled");
456
457     # StockRotationAdvance transfer added
458     my $transfer_requests = Koha::Item::Transfers->search(
459         {
460             itemnumber    => $sritem_1->item->itemnumber,
461             datearrived   => undef,
462             datecancelled => undef
463         }
464     );
465     is($transfer_requests->count, '2', "StockrotationAdvance transfer queued");
466
467     # Arrive at new branch
468     $transfer_request->datearrived(dt_from_string())->store;
469     $sritem_1->item->holdingbranch($srstage_2->branchcode_id)->store;
470
471     # StockRotationAdvance transfer added
472     $transfer_request = $sritem_1->item->get_transfer;
473     is($transfer_request->reason, 'StockrotationAdvance', "StockrotationAdvance transfer remains after reserve is met");
474     is($transfer_request->frombranch, $srstage_1->branchcode_id, "Origin correct.");
475     is($transfer_request->tobranch, $srstage_2->branchcode_id, "Target correct.");
476
477     # Arrive at new branch
478     $transfer_request->datearrived(dt_from_string())->store;
479     $sritem_1->item->holdingbranch($srstage_2->branchcode_id)->store;
480
481     # Checked out item, advanced to next stage, checkedout from next stage
482     # transfer should be generated, but not initiated
483     my $issue = $builder->build_object({
484         class => 'Koha::Checkouts',
485         value => {
486              branchcode => $srstage_1->branchcode_id,
487              itemnumber => $sritem_1->item->itemnumber,
488              returndate => undef
489         }
490     });
491     $sritem_1->item->holdingbranch($srstage_1->branchcode_id)->store;
492     ok($sritem_1->advance, "Advancement done.");
493     $transfer_request = $sritem_1->item->get_transfer;
494     is($transfer_request->frombranch, $srstage_1->branchcode_id, "Origin correct.");
495     is($transfer_request->tobranch, $srstage_1->branchcode_id, "Target correct.");
496     is($transfer_request->datesent, undef, "Transfer waiting to initiate until return.");
497
498     $issue->delete; #remove issue
499     $sritem_1->advance; #advance back to second stage
500     # Set arrived
501     $transfer_request->datearrived(dt_from_string())->store;
502     $sritem_1->item->holdingbranch($srstage_2->branchcode_id)->store;
503
504
505     $srstage_1->rota->cyclical(0)->store;         # Set Rota to non-cyclical.
506
507     my $srstage_3 = $builder->build_object({
508         class => 'Koha::StockRotationStages',
509         value => { duration => 50 }
510     });
511     $srstage_3->discard_changes;
512     $srstage_3->move_to_group($sritem_1->stage->rota_id);
513     $srstage_3->move_last;
514
515     # Advance again, to end of rota.
516     ok($sritem_1->advance, "Non-cyclical advance to last stage.");
517
518     # Arrive at new branch
519     $transfer_request->datearrived(dt_from_string())->store;
520     $sritem_1->item->holdingbranch($srstage_3->branchcode_id)->store;
521
522     # Advance again, Remove from rota.
523     ok($sritem_1->advance, "Non-cyclical advance.");
524     ## Refetch sritem_1
525     $sritem_1 = Koha::StockRotationItems->find({ itemnumber_id => $item_id });
526     is($sritem_1, undef, "StockRotationItem has been removed.");
527     $item = Koha::Items->find($item_id);
528     is($item->homebranch, $srstage_3->branchcode_id, "Item homebranch remains");
529
530     $schema->storage->txn_rollback;
531 };
532
533 subtest "Tests for investigate (singular)." => sub {
534     plan tests => 7;
535     $schema->storage->txn_begin;
536
537     # Test brand new item's investigation ['initiation']
538     my $sritem = $builder->build(
539         {
540             source => 'Stockrotationitem',
541             value  => {
542                 fresh         => 1,
543                 itemnumber_id => $builder->build_sample_item->itemnumber
544             }
545         }
546     );
547     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
548     is($dbitem->investigate->{reason}, 'initiation', "fresh item initiates.");
549
550     # Test brand new item at stagebranch ['initiation']
551     $sritem = $builder->build(
552         {
553             source => 'Stockrotationitem',
554             value  => {
555                 fresh         => 1,
556                 itemnumber_id => $builder->build_sample_item->itemnumber
557             }
558         }
559     );
560     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
561     $dbitem->item->homebranch($dbitem->stage->branchcode_id)->store;
562     $dbitem->item->holdingbranch($dbitem->stage->branchcode_id)->store;
563     is($dbitem->investigate->{reason}, 'initiation', "fresh item at stagebranch initiates.");
564
565     # Test item not at stagebranch with branchtransfer history ['repatriation']
566     $sritem = $builder->build(
567         {
568             source => 'Stockrotationitem',
569             value  => {
570                 'fresh'       => 0,
571                 itemnumber_id => $builder->build_sample_item->itemnumber
572             }
573         }
574     );
575     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
576     my $dbtransfer = Koha::Item::Transfer->new({
577         'itemnumber'  => $dbitem->itemnumber_id,
578         'frombranch'  => $dbitem->item->homebranch,
579         'tobranch'    => $dbitem->item->homebranch,
580         'datesent'    => dt_from_string(),
581         'datearrived' => dt_from_string(),
582         'reason'      => "StockrotationAdvance",
583     })->store;
584     is($dbitem->investigate->{reason}, 'repatriation', "older item repatriates.");
585
586     # Test item at stagebranch with branchtransfer history ['not-ready']
587     $sritem = $builder->build(
588         {
589             source => 'Stockrotationitem',
590             value  => {
591                 'fresh'       => 0,
592                 itemnumber_id => $builder->build_sample_item->itemnumber
593             }
594         }
595     );
596     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
597     $dbtransfer = Koha::Item::Transfer->new({
598         'itemnumber'  => $dbitem->itemnumber_id,
599         'frombranch'  => $dbitem->item->homebranch,
600         'tobranch'    => $dbitem->stage->branchcode_id,
601         'datesent'    => dt_from_string(),
602         'datearrived' => dt_from_string(),
603         'reason'      => "StockrotationAdvance",
604     })->store;
605     $dbitem->item->homebranch($dbitem->stage->branchcode_id)->store;
606     $dbitem->item->holdingbranch($dbitem->stage->branchcode_id)->store;
607     is($dbitem->investigate->{reason}, 'not-ready', "older item at stagebranch not-ready.");
608
609     # Test item due for advancement ['advancement']
610     $sritem = $builder->build(
611         {
612             source => 'Stockrotationitem',
613             value  => {
614                 fresh         => 0,
615                 itemnumber_id => $builder->build_sample_item->itemnumber
616             }
617         }
618     );
619     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
620     $dbitem->indemand(0)->store;
621     $dbitem->stage->duration(50)->store;
622     my $sent_duration =  DateTime::Duration->new( days => 55);
623     my $arrived_duration =  DateTime::Duration->new( days => 52);
624     $dbtransfer = Koha::Item::Transfer->new({
625         'itemnumber'  => $dbitem->itemnumber_id,
626         'frombranch'  => $dbitem->item->homebranch,
627         'tobranch'    => $dbitem->stage->branchcode_id,
628         'datesent'    => dt_from_string() - $sent_duration,
629         'datearrived' => dt_from_string() - $arrived_duration,
630         'reason'      => "StockrotationAdvance",
631     })->store;
632     $dbitem->item->homebranch($dbitem->stage->branchcode_id)->store;
633     $dbitem->item->holdingbranch($dbitem->stage->branchcode_id)->store;
634     is($dbitem->investigate->{reason}, 'advancement',
635        "Item ready for advancement.");
636
637     # Test item due for advancement but in-demand ['in-demand']
638     $sritem = $builder->build(
639         {
640             source => 'Stockrotationitem',
641             value  => {
642                 fresh         => 0,
643                 itemnumber_id => $builder->build_sample_item->itemnumber
644             }
645         }
646     );
647     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
648     $dbitem->indemand(1)->store;
649     $dbitem->stage->duration(50)->store;
650     $sent_duration =  DateTime::Duration->new( days => 55);
651     $arrived_duration =  DateTime::Duration->new( days => 52);
652     $dbtransfer = Koha::Item::Transfer->new({
653         'itemnumber'  => $dbitem->itemnumber_id,
654         'frombranch'  => $dbitem->item->homebranch,
655         'tobranch'    => $dbitem->stage->branchcode_id,
656         'datesent'    => dt_from_string() - $sent_duration,
657         'datearrived' => dt_from_string() - $arrived_duration,
658         'reason'      => "StockrotationAdvance",
659     })->store;
660     $dbitem->item->homebranch($dbitem->stage->branchcode_id)->store;
661     $dbitem->item->holdingbranch($dbitem->stage->branchcode_id)->store;
662     is($dbitem->investigate->{reason}, 'in-demand',
663        "Item advances, but in-demand.");
664
665     # Test item ready for advancement, but at wrong library ['repatriation']
666     $sritem = $builder->build(
667         {
668             source => 'Stockrotationitem',
669             value  => {
670                 fresh         => 0,
671                 itemnumber_id => $builder->build_sample_item->itemnumber
672             }
673         }
674     );
675     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
676     $dbitem->indemand(0)->store;
677     $dbitem->stage->duration(50)->store;
678     $sent_duration =  DateTime::Duration->new( days => 55);
679     $arrived_duration =  DateTime::Duration->new( days => 52);
680     $dbtransfer = Koha::Item::Transfer->new({
681         'itemnumber'  => $dbitem->itemnumber_id,
682         'frombranch'  => $dbitem->item->homebranch,
683         'tobranch'    => $dbitem->stage->branchcode_id,
684         'datesent'    => dt_from_string() - $sent_duration,
685         'datearrived' => dt_from_string() - $arrived_duration,
686         'reason'      => "StockrotationAdvance",
687     })->store;
688     is($dbitem->investigate->{reason}, 'repatriation',
689        "Item advances, but not at stage branch.");
690
691     $schema->storage->txn_rollback;
692 };
693
694 subtest "Tests for toggle_indemand" => sub {
695     plan tests => 15;
696     $schema->storage->txn_begin;
697
698     my $sritem = $builder->build({
699         source => 'Stockrotationitem',
700         value => { 'fresh' => 0, 'indemand' => 0 }
701     });
702     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
703     my $firstbranch = $dbitem->stage->branchcode_id;
704     $dbitem->item->holdingbranch($firstbranch)->store;
705     my $dbstage = $dbitem->stage;
706     $dbstage->position(1)->duration(50)->store; # Configure stage.
707     # Configure item
708     $dbitem->item->holdingbranch($firstbranch)->store;
709     $dbitem->item->homebranch($firstbranch)->store;
710     # Sanity check
711     is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage sanity check.");
712
713     # Test if an item is not in transfer, toggle always acts.
714     is($dbitem->indemand, 0, "Item not in transfer starts with indemand disabled.");
715     $dbitem->toggle_indemand;
716     is($dbitem->indemand, 1, "Item not in transfer toggled correctly first time.");
717     $dbitem->toggle_indemand;
718     is($dbitem->indemand, 0, "Item not in transfer toggled correctly second time.");
719
720     # Add stages
721     my $srstage = $builder->build({
722         source => 'Stockrotationstage',
723         value => { duration => 50 }
724     });
725     my $dbstage2 = Koha::StockRotationStages->find($srstage->{stage_id});
726     $dbstage2->move_to_group($dbitem->stage->rota_id);
727     $dbstage2->position(2)->store;
728     my $secondbranch = $dbstage2->branchcode_id;
729
730     # Test an item in transfer, toggle cancels transfer and resets indemand.
731     ok($dbitem->advance, "Advancement done.");
732     $dbitem->get_from_storage;
733     my $transfer = $dbitem->item->get_transfer;
734     is(ref($transfer), 'Koha::Item::Transfer', 'Item set to in transfer as expected');
735     is($transfer->frombranch, $firstbranch, 'Transfer from set correctly');
736     is($transfer->tobranch, $secondbranch, 'Transfer to set correctly');
737     is($transfer->datearrived, undef, 'Transfer datearrived not set');
738     $dbitem->toggle_indemand;
739     my $updated_transfer = $transfer->get_from_storage;
740     is($updated_transfer->frombranch, $firstbranch, 'Transfer from retained correctly');
741     is($updated_transfer->tobranch, $firstbranch, 'Transfer to updated correctly');
742     isnt($updated_transfer->datearrived, undef, 'Transfer datearrived set as expected');
743     is($dbitem->indemand, 0, "Item retains indemand as expected.");
744     is($dbitem->stage_id, $dbstage->id, 'Item stage reset as expected.');
745     is($dbitem->item->homebranch, $firstbranch, 'Item homebranch reset as expected.');
746
747     $schema->storage->txn_rollback;
748 };
749
750 1;