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;
25 use Koha::Item::Transfer;
26 use t::lib::TestBuilder;
29 use Test::More tests => 8;
31 my $schema = Koha::Database->new->schema;
33 use_ok('Koha::StockRotationItems');
34 use_ok('Koha::StockRotationItem');
36 my $builder = t::lib::TestBuilder->new;
38 subtest 'Basic object tests' => sub {
42 $schema->storage->txn_begin;
44 my $itm = $builder->build({ source => 'Item' });
45 my $stage = $builder->build({ source => 'Stockrotationstage' });
47 my $item = $builder->build({
48 source => 'Stockrotationitem',
50 itemnumber_id => $itm->{itemnumber},
51 stage_id => $stage->{stage_id},
55 my $sritem = Koha::StockRotationItems->find($item->{itemnumber_id});
58 'Koha::StockRotationItem',
59 "Correctly create and load a stock rotation item."
62 # Relationship to rota
63 isa_ok( $sritem->itemnumber, 'Koha::Item', "Fetched related item." );
64 is( $sritem->itemnumber->itemnumber, $itm->{itemnumber}, "Related rota OK." );
66 # Relationship to stage
67 isa_ok( $sritem->stage, 'Koha::StockRotationStage', "Fetched related stage." );
68 is( $sritem->stage->stage_id, $stage->{stage_id}, "Related stage OK." );
71 $schema->storage->txn_rollback;
74 subtest 'Tests for needs_repatriating' => sub {
78 $schema->storage->txn_begin;
80 # Setup a pristine stockrotation context.
81 my $sritem = $builder->build({ source => 'Stockrotationitem' });
82 my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
83 $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id);
84 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
85 $dbitem->stage->position(1);
87 my $dbrota = $dbitem->stage->rota;
88 my $newstage = $builder->build({
89 source => 'Stockrotationstage',
91 rota_id => $dbrota->rota_id,
96 # - homebranch == holdingbranch [0]
98 $dbitem->needs_repatriating, 0,
99 "Homebranch == Holdingbranch."
102 my $branch = $builder->build({ source => 'Branch' });
103 $dbitem->itemnumber->holdingbranch($branch->{branchcode});
105 # - homebranch != holdingbranch [1]
107 $dbitem->needs_repatriating, 1,
108 "Homebranch != holdingbranch."
111 # Set to incorrect homebranch.
112 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
113 $dbitem->itemnumber->homebranch($branch->{branchcode});
114 # - homebranch != stockrotationstage.branch & not in transit [1]
116 $dbitem->needs_repatriating, 1,
117 "Homebranch != StockRotationStage.Branchcode_id & not in transit."
120 # Set to in transit (by implication).
121 $dbitem->stage($newstage->{stage_id});
122 # - homebranch != stockrotaitonstage.branch & in transit [0]
124 $dbitem->needs_repatriating, 1,
125 "homebranch != stockrotaitonstage.branch & in transit."
128 $schema->storage->txn_rollback;
131 subtest "Tests for repatriate." => sub {
133 $schema->storage->txn_begin;
134 my $sritem = $builder->build({ source => 'Stockrotationitem' });
135 my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
136 $dbitem->stage->position(1);
137 $dbitem->stage->duration(50);
138 my $branch = $builder->build({ source => 'Branch' });
139 $dbitem->itemnumber->holdingbranch($branch->{branchcode});
141 # Test a straight up repatriate
142 ok($dbitem->repatriate, "Repatriation done.");
143 my $intransfer = $dbitem->itemnumber->get_transfer;
144 is($intransfer->frombranch, $branch->{branchcode}, "Origin correct.");
145 is($intransfer->tobranch, $dbitem->stage->branchcode_id, "Target Correct.");
147 $schema->storage->txn_rollback;
150 subtest "Tests for needs_advancing." => sub {
152 $schema->storage->txn_begin;
154 # Test behaviour of item freshly added to rota.
155 my $sritem = $builder->build({
156 source => 'Stockrotationitem',
157 value => { 'fresh' => 1, },
159 my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
160 is($dbitem->needs_advancing, 1, "An item that is fresh will always need advancing.");
162 # Setup a pristine stockrotation context.
163 $sritem = $builder->build({
164 source => 'Stockrotationitem',
165 value => { 'fresh' => 0,}
167 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
168 $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id);
169 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
170 $dbitem->stage->position(1);
171 $dbitem->stage->duration(50);
173 my $dbtransfer = Koha::Item::Transfer->new({
174 'itemnumber' => $dbitem->itemnumber_id,
175 'frombranch' => $dbitem->stage->branchcode_id,
176 'tobranch' => $dbitem->stage->branchcode_id,
177 'datesent' => DateTime->now,
178 'datearrived' => undef,
179 'reason' => "StockrotationAdvance",
182 # Test item will not be advanced if in transit.
183 is($dbitem->needs_advancing, 0, "Not ready to advance: in transfer.");
184 # Test item will not be advanced if in transit even if fresh.
185 $dbitem->fresh(1)->store;
186 is($dbitem->needs_advancing, 0, "Not ready to advance: in transfer (fresh).");
187 $dbitem->fresh(0)->store;
189 # Test item will not be advanced if it has not spent enough time.
190 $dbtransfer->datearrived(DateTime->now)->store;
191 is($dbitem->needs_advancing, 0, "Not ready to advance: Not spent enough time.");
192 # Test item will be advanced if it has not spent enough time, but is fresh.
193 $dbitem->fresh(1)->store;
194 is($dbitem->needs_advancing, 1, "Advance: Not spent enough time, but fresh.");
195 $dbitem->fresh(0)->store;
197 # Test item will be advanced if it has spent enough time.
198 $dbtransfer->datesent( # Item was sent 100 days ago...
199 DateTime->now - DateTime::Duration->new( days => 100 )
201 $dbtransfer->datearrived( # And arrived 75 days ago.
202 DateTime->now - DateTime::Duration->new( days => 75 )
204 is($dbitem->needs_advancing, 1, "Ready to be advanced.");
206 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.";
208 $schema->storage->txn_rollback;
211 subtest "Tests for advance." => sub {
213 $schema->storage->txn_begin;
215 my $sritem = $builder->build({
216 source => 'Stockrotationitem',
217 value => { 'fresh' => 1 }
219 my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
220 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
221 my $dbstage = $dbitem->stage;
222 $dbstage->position(1)->duration(50)->store; # Configure stage.
224 $dbitem->itemnumber->holdingbranch($dbstage->branchcode_id)->store;
225 $dbitem->itemnumber->homebranch($dbstage->branchcode_id)->store;
227 is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage sanity check.");
229 # Test if an item is fresh, always move to first stage.
230 is($dbitem->fresh, 1, "Fresh is correct.");
232 is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage is first stage after fresh advance.");
233 is($dbitem->fresh, 0, "Fresh reset after advance.");
235 # Test cases of single stage
236 $dbstage->rota->cyclical(1)->store; # Set Rota to cyclical.
237 ok($dbitem->advance, "Single stage cyclical advance done.");
239 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
240 is($dbitem->stage->stage_id, $dbstage->stage_id, "Single stage cyclical stage OK.");
242 # Test with indemand advance
243 $dbitem->indemand(1)->store;
244 ok($dbitem->advance, "Indemand item advance done.");
246 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
247 is($dbitem->indemand, 0, "Indemand OK.");
248 is($dbitem->stage->stage_id, $dbstage->stage_id, "Indemand item advance stage OK.");
251 my $srstage = $builder->build({
252 source => 'Stockrotationstage',
253 value => { duration => 50 }
255 my $dbstage2 = Koha::StockRotationStages->find($srstage->{stage_id});
256 $dbstage2->move_to_group($dbitem->stage->rota_id);
257 $dbstage2->move_last;
259 # Test a straight up advance
260 ok($dbitem->advance, "Advancement done.");
262 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
264 is($dbitem->stage->stage_id, $dbstage2->stage_id, "Stage updated.");
265 my $intransfer = $dbitem->itemnumber->get_transfer;
266 is($intransfer->frombranch, $dbstage->branchcode_id, "Origin correct.");
267 is($intransfer->tobranch, $dbstage2->branchcode_id, "Target Correct.");
269 $dbstage->rota->cyclical(0)->store; # Set Rota to non-cyclical.
271 # Arrive at new branch
272 $intransfer->datearrived(DateTime->now)->store;
273 $dbitem->itemnumber->holdingbranch($srstage->{branchcode_id})->store;
274 $dbitem->itemnumber->homebranch($srstage->{branchcode_id})->store;
276 # Advance again, Remove from rota.
277 ok($dbitem->advance, "Non-cyclical advance.");
279 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
280 is($dbitem, undef, "StockRotationItem has been removed.");
282 $schema->storage->txn_rollback;
285 subtest "Tests for investigate (singular)." => sub {
287 $schema->storage->txn_begin;
289 # Test brand new item's investigation ['initiation']
290 my $sritem = $builder->build({ source => 'Stockrotationitem', value => { fresh => 1 } });
291 my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
292 is($dbitem->investigate->{reason}, 'initiation', "fresh item initiates.");
294 # Test brand new item at stagebranch ['initiation']
295 $sritem = $builder->build({ source => 'Stockrotationitem', value => { fresh => 1 } });
296 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
297 $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
298 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
299 is($dbitem->investigate->{reason}, 'initiation', "fresh item at stagebranch initiates.");
301 # Test item not at stagebranch with branchtransfer history ['repatriation']
302 $sritem = $builder->build({
303 source => 'Stockrotationitem',
304 value => { 'fresh' => 0,}
306 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
307 my $dbtransfer = Koha::Item::Transfer->new({
308 'itemnumber' => $dbitem->itemnumber_id,
309 'frombranch' => $dbitem->itemnumber->homebranch,
310 'tobranch' => $dbitem->itemnumber->homebranch,
311 'datesent' => DateTime->now,
312 'datearrived' => DateTime->now,
313 'reason' => "StockrotationAdvance",
315 is($dbitem->investigate->{reason}, 'repatriation', "older item repatriates.");
317 # Test item at stagebranch with branchtransfer history ['not-ready']
318 $sritem = $builder->build({
319 source => 'Stockrotationitem',
320 value => { 'fresh' => 0,}
322 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
323 $dbtransfer = Koha::Item::Transfer->new({
324 'itemnumber' => $dbitem->itemnumber_id,
325 'frombranch' => $dbitem->itemnumber->homebranch,
326 'tobranch' => $dbitem->stage->branchcode_id,
327 'datesent' => DateTime->now,
328 'datearrived' => DateTime->now,
329 'reason' => "StockrotationAdvance",
331 $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
332 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
333 is($dbitem->investigate->{reason}, 'not-ready', "older item at stagebranch not-ready.");
335 # Test item due for advancement ['advancement']
336 $sritem = $builder->build({ source => 'Stockrotationitem', value => { fresh => 0 } });
337 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
338 $dbitem->indemand(0)->store;
339 $dbitem->stage->duration(50)->store;
340 my $sent_duration = DateTime::Duration->new( days => 55);
341 my $arrived_duration = DateTime::Duration->new( days => 52);
342 $dbtransfer = Koha::Item::Transfer->new({
343 'itemnumber' => $dbitem->itemnumber_id,
344 'frombranch' => $dbitem->itemnumber->homebranch,
345 'tobranch' => $dbitem->stage->branchcode_id,
346 'datesent' => DateTime->now - $sent_duration,
347 'datearrived' => DateTime->now - $arrived_duration,
348 'reason' => "StockrotationAdvance",
350 $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
351 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
352 is($dbitem->investigate->{reason}, 'advancement',
353 "Item ready for advancement.");
355 # Test item due for advancement but in-demand ['in-demand']
356 $sritem = $builder->build({ source => 'Stockrotationitem', value => { fresh => 0 } });
357 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
358 $dbitem->indemand(1)->store;
359 $dbitem->stage->duration(50)->store;
360 $sent_duration = DateTime::Duration->new( days => 55);
361 $arrived_duration = DateTime::Duration->new( days => 52);
362 $dbtransfer = Koha::Item::Transfer->new({
363 'itemnumber' => $dbitem->itemnumber_id,
364 'frombranch' => $dbitem->itemnumber->homebranch,
365 'tobranch' => $dbitem->stage->branchcode_id,
366 'datesent' => DateTime->now - $sent_duration,
367 'datearrived' => DateTime->now - $arrived_duration,
368 'reason' => "StockrotationAdvance",
370 $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
371 $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
372 is($dbitem->investigate->{reason}, 'in-demand',
373 "Item advances, but in-demand.");
375 # Test item ready for advancement, but at wrong library ['repatriation']
376 $sritem = $builder->build({ source => 'Stockrotationitem', value => { fresh => 0 } });
377 $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
378 $dbitem->indemand(0)->store;
379 $dbitem->stage->duration(50)->store;
380 $sent_duration = DateTime::Duration->new( days => 55);
381 $arrived_duration = DateTime::Duration->new( days => 52);
382 $dbtransfer = Koha::Item::Transfer->new({
383 'itemnumber' => $dbitem->itemnumber_id,
384 'frombranch' => $dbitem->itemnumber->homebranch,
385 'tobranch' => $dbitem->stage->branchcode_id,
386 'datesent' => DateTime->now - $sent_duration,
387 'datearrived' => DateTime->now - $arrived_duration,
388 'reason' => "StockrotationAdvance",
390 is($dbitem->investigate->{reason}, 'repatriation',
391 "Item advances, but not at stage branch.");
393 $schema->storage->txn_rollback;