3 # Copyright PTFS Europe 2016
5 # This file is part of Koha.
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.
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.
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>.
23 use DateTime::Duration;
26 use Koha::Item::Transfer;
27 use t::lib::TestBuilder;
30 use Test::More tests => 8;
32 my $schema = Koha::Database->new->schema;
34 use_ok('Koha::StockRotationItems');
35 use_ok('Koha::StockRotationItem');
37 my $builder = t::lib::TestBuilder->new;
39 subtest 'Basic object tests' => sub {
43 $schema->storage->txn_begin;
45 my $itm = $builder->build_sample_item;
46 my $stage = $builder->build({ source => 'Stockrotationstage' });
48 my $item = $builder->build({
49 source => 'Stockrotationitem',
51 itemnumber_id => $itm->itemnumber,
52 stage_id => $stage->{stage_id},
56 my $sritem = Koha::StockRotationItems->find($item->{itemnumber_id});
59 'Koha::StockRotationItem',
60 "Correctly create and load a stock rotation item."
63 # Relationship to rota
64 isa_ok( $sritem->itemnumber, 'Koha::Item', "Fetched related item." );
65 is( $sritem->itemnumber->itemnumber, $itm->itemnumber, "Related rota OK." );
67 # Relationship to stage
68 isa_ok( $sritem->stage, 'Koha::StockRotationStage', "Fetched related stage." );
69 is( $sritem->stage->stage_id, $stage->{stage_id}, "Related stage OK." );
72 $schema->storage->txn_rollback;
75 subtest 'Tests for needs_repatriating' => sub {
79 $schema->storage->txn_begin;
81 # Setup a pristine stockrotation context.
82 my $sritem = $builder->build(
84 source => 'Stockrotationitem',
86 { itemnumber_id => $builder->build_sample_item->itemnumber }
89 my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
90 $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id);
91 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
92 $dbitem->stage->position(1);
94 my $dbrota = $dbitem->stage->rota;
95 my $newstage = $builder->build({
96 source => 'Stockrotationstage',
98 rota_id => $dbrota->rota_id,
103 # - homebranch == holdingbranch [0]
105 $dbitem->needs_repatriating, 0,
106 "Homebranch == Holdingbranch."
109 my $branch = $builder->build({ source => 'Branch' });
110 $dbitem->itemnumber->holdingbranch($branch->{branchcode});
112 # - homebranch != holdingbranch [1]
114 $dbitem->needs_repatriating, 1,
115 "Homebranch != holdingbranch."
118 # Set to incorrect homebranch.
119 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
120 $dbitem->itemnumber->homebranch($branch->{branchcode});
121 # - homebranch != stockrotationstage.branch & not in transit [1]
123 $dbitem->needs_repatriating, 1,
124 "Homebranch != StockRotationStage.Branchcode_id & not in transit."
127 # Set to in transit (by implication).
128 $dbitem->stage($newstage->{stage_id});
129 # - homebranch != stockrotaitonstage.branch & in transit [0]
131 $dbitem->needs_repatriating, 1,
132 "homebranch != stockrotaitonstage.branch & in transit."
135 $schema->storage->txn_rollback;
138 subtest "Tests for repatriate." => sub {
140 $schema->storage->txn_begin;
141 my $sritem = $builder->build(
143 source => 'Stockrotationitem',
145 { itemnumber_id => $builder->build_sample_item->itemnumber }
148 my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
149 $dbitem->stage->position(1);
150 $dbitem->stage->duration(50);
151 my $branch = $builder->build({ source => 'Branch' });
152 $dbitem->itemnumber->holdingbranch($branch->{branchcode});
154 # Test a straight up repatriate
155 ok($dbitem->repatriate, "Repatriation done.");
156 my $intransfer = $dbitem->itemnumber->get_transfer;
157 is($intransfer->frombranch, $branch->{branchcode}, "Origin correct.");
158 is($intransfer->tobranch, $dbitem->stage->branchcode_id, "Target Correct.");
160 $schema->storage->txn_rollback;
163 subtest "Tests for needs_advancing." => sub {
165 $schema->storage->txn_begin;
167 # Test behaviour of item freshly added to rota.
168 my $sritem = $builder->build(
170 source => 'Stockrotationitem',
173 itemnumber_id => $builder->build_sample_item->itemnumber
177 my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
178 is($dbitem->needs_advancing, 1, "An item that is fresh will always need advancing.");
180 # Setup a pristine stockrotation context.
181 $sritem = $builder->build(
183 source => 'Stockrotationitem',
186 itemnumber_id => $builder->build_sample_item->itemnumber
190 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
191 $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id);
192 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
193 $dbitem->stage->position(1);
194 $dbitem->stage->duration(50);
196 my $dbtransfer = Koha::Item::Transfer->new({
197 'itemnumber' => $dbitem->itemnumber_id,
198 'frombranch' => $dbitem->stage->branchcode_id,
199 'tobranch' => $dbitem->stage->branchcode_id,
200 'datesent' => dt_from_string(),
201 'datearrived' => undef,
202 'reason' => "StockrotationAdvance",
205 # Test item will not be advanced if in transit.
206 is($dbitem->needs_advancing, 0, "Not ready to advance: in transfer.");
207 # Test item will not be advanced if in transit even if fresh.
208 $dbitem->fresh(1)->store;
209 is($dbitem->needs_advancing, 0, "Not ready to advance: in transfer (fresh).");
210 $dbitem->fresh(0)->store;
212 # Test item will not be advanced if it has not spent enough time.
213 $dbtransfer->datearrived(dt_from_string())->store;
214 is($dbitem->needs_advancing, 0, "Not ready to advance: Not spent enough time.");
215 # Test item will be advanced if it has not spent enough time, but is fresh.
216 $dbitem->fresh(1)->store;
217 is($dbitem->needs_advancing, 1, "Advance: Not spent enough time, but fresh.");
218 $dbitem->fresh(0)->store;
220 # Test item will be advanced if it has spent enough time.
221 $dbtransfer->datesent( # Item was sent 100 days ago...
222 dt_from_string() - DateTime::Duration->new( days => 100 )
224 $dbtransfer->datearrived( # And arrived 75 days ago.
225 dt_from_string() - DateTime::Duration->new( days => 75 )
227 is($dbitem->needs_advancing, 1, "Ready to be advanced.");
229 warning_is {$dbitem->needs_advancing} "We have no historical branch transfer for itemnumber " . $dbitem->itemnumber->itemnumber . "; This should not have happened!", "Missing transfer is warned.";
231 $schema->storage->txn_rollback;
234 subtest "Tests for advance." => sub {
236 $schema->storage->txn_begin;
238 my $sritem = $builder->build(
240 source => 'Stockrotationitem',
243 itemnumber_id => $builder->build_sample_item->itemnumber
247 my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
248 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
249 my $dbstage = $dbitem->stage;
250 $dbstage->position(1)->duration(50)->store; # Configure stage.
252 $dbitem->itemnumber->holdingbranch($dbstage->branchcode_id)->store;
253 $dbitem->itemnumber->homebranch($dbstage->branchcode_id)->store;
255 is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage sanity check.");
257 # Test if an item is fresh, always move to first stage.
258 is($dbitem->fresh, 1, "Fresh is correct.");
260 is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage is first stage after fresh advance.");
261 is($dbitem->fresh, 0, "Fresh reset after advance.");
263 # Test cases of single stage
264 $dbstage->rota->cyclical(1)->store; # Set Rota to cyclical.
265 ok($dbitem->advance, "Single stage cyclical advance done.");
267 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
268 is($dbitem->stage->stage_id, $dbstage->stage_id, "Single stage cyclical stage OK.");
270 # Test with indemand advance
271 $dbitem->indemand(1)->store;
272 ok($dbitem->advance, "Indemand item advance done.");
274 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
275 is($dbitem->indemand, 0, "Indemand OK.");
276 is($dbitem->stage->stage_id, $dbstage->stage_id, "Indemand item advance stage OK.");
279 my $srstage = $builder->build({
280 source => 'Stockrotationstage',
281 value => { duration => 50 }
283 my $dbstage2 = Koha::StockRotationStages->find($srstage->{stage_id});
284 $dbstage2->move_to_group($dbitem->stage->rota_id);
285 $dbstage2->move_last;
287 # Test a straight up advance
288 ok($dbitem->advance, "Advancement done.");
290 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
292 is($dbitem->stage->stage_id, $dbstage2->stage_id, "Stage updated.");
294 $dbitem->itemnumber->homebranch,
295 $dbstage2->branchcode_id,
296 "Item homebranch updated"
298 my $intransfer = $dbitem->itemnumber->get_transfer;
299 is($intransfer->frombranch, $dbstage->branchcode_id, "Origin correct.");
300 is($intransfer->tobranch, $dbstage2->branchcode_id, "Target Correct.");
302 # Arrive at new branch
303 $intransfer->datearrived(dt_from_string())->store;
304 $dbitem->itemnumber->holdingbranch($srstage->{branchcode_id})->store;
306 # Test a cyclical advance
307 ok($dbitem->advance, "Cyclical advancement done.");
309 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
311 is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage updated.");
313 $dbitem->itemnumber->homebranch,
314 $dbstage->branchcode_id,
315 "Item homebranch updated"
317 $intransfer = $dbitem->itemnumber->get_transfer;
318 is($intransfer->frombranch, $dbstage2->branchcode_id, "Origin correct.");
319 is($intransfer->tobranch, $dbstage->branchcode_id, "Target correct.");
321 # Arrive at new branch
322 $intransfer->datearrived(dt_from_string())->store;
323 $dbitem->itemnumber->holdingbranch($srstage->{branchcode_id})->store;
325 $dbstage->rota->cyclical(0)->store; # Set Rota to non-cyclical.
327 # Advance again, to end of rota.
328 ok($dbitem->advance, "Non-cyclical advance to last stage.");
330 # Arrive at new branch
331 $intransfer->datearrived(dt_from_string())->store;
332 $dbitem->itemnumber->holdingbranch($srstage->{branchcode_id})->store;
334 # Advance again, Remove from rota.
335 ok($dbitem->advance, "Non-cyclical advance.");
337 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
338 is($dbitem, undef, "StockRotationItem has been removed.");
339 my $item = Koha::Items->find($sritem->{itemnumber_id});
340 is($item->homebranch, $srstage->{branchcode_id}, "Item homebranch remains");
342 $schema->storage->txn_rollback;
345 subtest "Tests for investigate (singular)." => sub {
347 $schema->storage->txn_begin;
349 # Test brand new item's investigation ['initiation']
350 my $sritem = $builder->build(
352 source => 'Stockrotationitem',
355 itemnumber_id => $builder->build_sample_item->itemnumber
359 my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
360 is($dbitem->investigate->{reason}, 'initiation', "fresh item initiates.");
362 # Test brand new item at stagebranch ['initiation']
363 $sritem = $builder->build(
365 source => 'Stockrotationitem',
368 itemnumber_id => $builder->build_sample_item->itemnumber
372 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
373 $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
374 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
375 is($dbitem->investigate->{reason}, 'initiation', "fresh item at stagebranch initiates.");
377 # Test item not at stagebranch with branchtransfer history ['repatriation']
378 $sritem = $builder->build(
380 source => 'Stockrotationitem',
383 itemnumber_id => $builder->build_sample_item->itemnumber
387 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
388 my $dbtransfer = Koha::Item::Transfer->new({
389 'itemnumber' => $dbitem->itemnumber_id,
390 'frombranch' => $dbitem->itemnumber->homebranch,
391 'tobranch' => $dbitem->itemnumber->homebranch,
392 'datesent' => dt_from_string(),
393 'datearrived' => dt_from_string(),
394 'reason' => "StockrotationAdvance",
396 is($dbitem->investigate->{reason}, 'repatriation', "older item repatriates.");
398 # Test item at stagebranch with branchtransfer history ['not-ready']
399 $sritem = $builder->build(
401 source => 'Stockrotationitem',
404 itemnumber_id => $builder->build_sample_item->itemnumber
408 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
409 $dbtransfer = Koha::Item::Transfer->new({
410 'itemnumber' => $dbitem->itemnumber_id,
411 'frombranch' => $dbitem->itemnumber->homebranch,
412 'tobranch' => $dbitem->stage->branchcode_id,
413 'datesent' => dt_from_string(),
414 'datearrived' => dt_from_string(),
415 'reason' => "StockrotationAdvance",
417 $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
418 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
419 is($dbitem->investigate->{reason}, 'not-ready', "older item at stagebranch not-ready.");
421 # Test item due for advancement ['advancement']
422 $sritem = $builder->build(
424 source => 'Stockrotationitem',
427 itemnumber_id => $builder->build_sample_item->itemnumber
431 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
432 $dbitem->indemand(0)->store;
433 $dbitem->stage->duration(50)->store;
434 my $sent_duration = DateTime::Duration->new( days => 55);
435 my $arrived_duration = DateTime::Duration->new( days => 52);
436 $dbtransfer = Koha::Item::Transfer->new({
437 'itemnumber' => $dbitem->itemnumber_id,
438 'frombranch' => $dbitem->itemnumber->homebranch,
439 'tobranch' => $dbitem->stage->branchcode_id,
440 'datesent' => dt_from_string() - $sent_duration,
441 'datearrived' => dt_from_string() - $arrived_duration,
442 'reason' => "StockrotationAdvance",
444 $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
445 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
446 is($dbitem->investigate->{reason}, 'advancement',
447 "Item ready for advancement.");
449 # Test item due for advancement but in-demand ['in-demand']
450 $sritem = $builder->build(
452 source => 'Stockrotationitem',
455 itemnumber_id => $builder->build_sample_item->itemnumber
459 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
460 $dbitem->indemand(1)->store;
461 $dbitem->stage->duration(50)->store;
462 $sent_duration = DateTime::Duration->new( days => 55);
463 $arrived_duration = DateTime::Duration->new( days => 52);
464 $dbtransfer = Koha::Item::Transfer->new({
465 'itemnumber' => $dbitem->itemnumber_id,
466 'frombranch' => $dbitem->itemnumber->homebranch,
467 'tobranch' => $dbitem->stage->branchcode_id,
468 'datesent' => dt_from_string() - $sent_duration,
469 'datearrived' => dt_from_string() - $arrived_duration,
470 'reason' => "StockrotationAdvance",
472 $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
473 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
474 is($dbitem->investigate->{reason}, 'in-demand',
475 "Item advances, but in-demand.");
477 # Test item ready for advancement, but at wrong library ['repatriation']
478 $sritem = $builder->build(
480 source => 'Stockrotationitem',
483 itemnumber_id => $builder->build_sample_item->itemnumber
487 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
488 $dbitem->indemand(0)->store;
489 $dbitem->stage->duration(50)->store;
490 $sent_duration = DateTime::Duration->new( days => 55);
491 $arrived_duration = DateTime::Duration->new( days => 52);
492 $dbtransfer = Koha::Item::Transfer->new({
493 'itemnumber' => $dbitem->itemnumber_id,
494 'frombranch' => $dbitem->itemnumber->homebranch,
495 'tobranch' => $dbitem->stage->branchcode_id,
496 'datesent' => dt_from_string() - $sent_duration,
497 'datearrived' => dt_from_string() - $arrived_duration,
498 'reason' => "StockrotationAdvance",
500 is($dbitem->investigate->{reason}, 'repatriation',
501 "Item advances, but not at stage branch.");
503 $schema->storage->txn_rollback;