Bug 26481: Add Koha::Item::Transfer->in_transit method
[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::Item::Transfer;
26 use t::lib::TestBuilder;
27 use Test::Warn;
28
29 use Test::More tests => 8;
30
31 my $schema = Koha::Database->new->schema;
32
33 use_ok('Koha::StockRotationItems');
34 use_ok('Koha::StockRotationItem');
35
36 my $builder = t::lib::TestBuilder->new;
37
38 subtest 'Basic object tests' => sub {
39
40     plan tests => 5;
41
42     $schema->storage->txn_begin;
43
44     my $itm = $builder->build_sample_item;
45     my $stage = $builder->build({ source => 'Stockrotationstage' });
46
47     my $item = $builder->build({
48         source => 'Stockrotationitem',
49         value  => {
50             itemnumber_id => $itm->itemnumber,
51             stage_id      => $stage->{stage_id},
52         },
53     });
54
55     my $sritem = Koha::StockRotationItems->find($item->{itemnumber_id});
56     isa_ok(
57         $sritem,
58         'Koha::StockRotationItem',
59         "Correctly create and load a stock rotation item."
60     );
61
62     # Relationship to rota
63     isa_ok( $sritem->itemnumber, 'Koha::Item', "Fetched related item." );
64     is( $sritem->itemnumber->itemnumber, $itm->itemnumber, "Related rota OK." );
65
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." );
69
70
71     $schema->storage->txn_rollback;
72 };
73
74 subtest 'Tests for needs_repatriating' => sub {
75
76     plan tests => 4;
77
78     $schema->storage->txn_begin;
79
80     # Setup a pristine stockrotation context.
81     my $sritem = $builder->build(
82         {
83             source => 'Stockrotationitem',
84             value =>
85               { itemnumber_id => $builder->build_sample_item->itemnumber }
86         }
87     );
88     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
89     $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id);
90     $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
91     $dbitem->stage->position(1);
92
93     my $dbrota = $dbitem->stage->rota;
94     my $newstage = $builder->build({
95         source => 'Stockrotationstage',
96         value => {
97             rota_id => $dbrota->rota_id,
98             position => 2,
99         }
100     });
101
102     # - homebranch == holdingbranch [0]
103     is(
104         $dbitem->needs_repatriating, 0,
105         "Homebranch == Holdingbranch."
106     );
107
108     my $branch = $builder->build({ source => 'Branch' });
109     $dbitem->itemnumber->holdingbranch($branch->{branchcode});
110
111     # - homebranch != holdingbranch [1]
112     is(
113         $dbitem->needs_repatriating, 1,
114         "Homebranch != holdingbranch."
115     );
116
117     # Set to incorrect homebranch.
118     $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
119     $dbitem->itemnumber->homebranch($branch->{branchcode});
120     # - homebranch != stockrotationstage.branch & not in transit [1]
121     is(
122         $dbitem->needs_repatriating, 1,
123         "Homebranch != StockRotationStage.Branchcode_id & not in transit."
124     );
125
126     # Set to in transit (by implication).
127     $dbitem->stage($newstage->{stage_id});
128     # - homebranch != stockrotaitonstage.branch & in transit [0]
129     is(
130         $dbitem->needs_repatriating, 1,
131         "homebranch != stockrotaitonstage.branch & in transit."
132     );
133
134     $schema->storage->txn_rollback;
135 };
136
137 subtest "Tests for repatriate." => sub {
138     plan tests => 3;
139     $schema->storage->txn_begin;
140     my $sritem = $builder->build(
141         {
142             source => 'Stockrotationitem',
143             value =>
144               { itemnumber_id => $builder->build_sample_item->itemnumber }
145         }
146     );
147     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
148     $dbitem->stage->position(1);
149     $dbitem->stage->duration(50);
150     my $branch = $builder->build({ source => 'Branch' });
151     $dbitem->itemnumber->holdingbranch($branch->{branchcode});
152
153     # Test a straight up repatriate
154     ok($dbitem->repatriate, "Repatriation done.");
155     my $intransfer = $dbitem->itemnumber->get_transfer;
156     is($intransfer->frombranch, $branch->{branchcode}, "Origin correct.");
157     is($intransfer->tobranch, $dbitem->stage->branchcode_id, "Target Correct.");
158
159     $schema->storage->txn_rollback;
160 };
161
162 subtest "Tests for needs_advancing." => sub {
163     plan tests => 7;
164     $schema->storage->txn_begin;
165
166     # Test behaviour of item freshly added to rota.
167     my $sritem = $builder->build(
168         {
169             source => 'Stockrotationitem',
170             value  => {
171                 'fresh'       => 1,
172                 itemnumber_id => $builder->build_sample_item->itemnumber
173             },
174         }
175     );
176     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
177     is($dbitem->needs_advancing, 1, "An item that is fresh will always need advancing.");
178
179     # Setup a pristine stockrotation context.
180     $sritem = $builder->build(
181         {
182             source => 'Stockrotationitem',
183             value  => {
184                 'fresh'       => 0,
185                 itemnumber_id => $builder->build_sample_item->itemnumber
186             }
187         }
188     );
189     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
190     $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id);
191     $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
192     $dbitem->stage->position(1);
193     $dbitem->stage->duration(50);
194
195     my $dbtransfer = Koha::Item::Transfer->new({
196         'itemnumber'  => $dbitem->itemnumber_id,
197         'frombranch'  => $dbitem->stage->branchcode_id,
198         'tobranch'    => $dbitem->stage->branchcode_id,
199         'datesent'    => DateTime->now,
200         'datearrived' => undef,
201         'reason'      => "StockrotationAdvance",
202     })->store;
203
204     # Test item will not be advanced if in transit.
205     is($dbitem->needs_advancing, 0, "Not ready to advance: in transfer.");
206     # Test item will not be advanced if in transit even if fresh.
207     $dbitem->fresh(1)->store;
208     is($dbitem->needs_advancing, 0, "Not ready to advance: in transfer (fresh).");
209     $dbitem->fresh(0)->store;
210
211     # Test item will not be advanced if it has not spent enough time.
212     $dbtransfer->datearrived(DateTime->now)->store;
213     is($dbitem->needs_advancing, 0, "Not ready to advance: Not spent enough time.");
214     # Test item will be advanced if it has not spent enough time, but is fresh.
215     $dbitem->fresh(1)->store;
216     is($dbitem->needs_advancing, 1, "Advance: Not spent enough time, but fresh.");
217     $dbitem->fresh(0)->store;
218
219     # Test item will be advanced if it has spent enough time.
220     $dbtransfer->datesent(      # Item was sent 100 days ago...
221         DateTime->now - DateTime::Duration->new( days => 100 )
222     )->store;
223     $dbtransfer->datearrived(   # And arrived 75 days ago.
224         DateTime->now - DateTime::Duration->new( days => 75 )
225     )->store;
226     is($dbitem->needs_advancing, 1, "Ready to be advanced.");
227     $dbtransfer->delete;
228     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.";
229
230     $schema->storage->txn_rollback;
231 };
232
233 subtest "Tests for advance." => sub {
234     plan tests => 15;
235     $schema->storage->txn_begin;
236
237     my $sritem = $builder->build(
238         {
239             source => 'Stockrotationitem',
240             value  => {
241                 'fresh'       => 1,
242                 itemnumber_id => $builder->build_sample_item->itemnumber
243             }
244         }
245     );
246     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
247     $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
248     my $dbstage = $dbitem->stage;
249     $dbstage->position(1)->duration(50)->store; # Configure stage.
250     # Configure item
251     $dbitem->itemnumber->holdingbranch($dbstage->branchcode_id)->store;
252     $dbitem->itemnumber->homebranch($dbstage->branchcode_id)->store;
253     # Sanity check
254     is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage sanity check.");
255
256     # Test if an item is fresh, always move to first stage.
257     is($dbitem->fresh, 1, "Fresh is correct.");
258     $dbitem->advance;
259     is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage is first stage after fresh advance.");
260     is($dbitem->fresh, 0, "Fresh reset after advance.");
261
262     # Test cases of single stage
263     $dbstage->rota->cyclical(1)->store;         # Set Rota to cyclical.
264     ok($dbitem->advance, "Single stage cyclical advance done.");
265     ## Refetch dbitem
266     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
267     is($dbitem->stage->stage_id, $dbstage->stage_id, "Single stage cyclical stage OK.");
268
269     # Test with indemand advance
270     $dbitem->indemand(1)->store;
271     ok($dbitem->advance, "Indemand item advance done.");
272     ## Refetch dbitem
273     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
274     is($dbitem->indemand, 0, "Indemand OK.");
275     is($dbitem->stage->stage_id, $dbstage->stage_id, "Indemand item advance stage OK.");
276
277     # Multi stages
278     my $srstage = $builder->build({
279         source => 'Stockrotationstage',
280         value => { duration => 50 }
281     });
282     my $dbstage2 = Koha::StockRotationStages->find($srstage->{stage_id});
283     $dbstage2->move_to_group($dbitem->stage->rota_id);
284     $dbstage2->move_last;
285
286     # Test a straight up advance
287     ok($dbitem->advance, "Advancement done.");
288     ## Refetch dbitem
289     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
290     ## Test results
291     is($dbitem->stage->stage_id, $dbstage2->stage_id, "Stage updated.");
292     my $intransfer = $dbitem->itemnumber->get_transfer;
293     is($intransfer->frombranch, $dbstage->branchcode_id, "Origin correct.");
294     is($intransfer->tobranch, $dbstage2->branchcode_id, "Target Correct.");
295
296     $dbstage->rota->cyclical(0)->store;         # Set Rota to non-cyclical.
297
298     # Arrive at new branch
299     $intransfer->datearrived(DateTime->now)->store;
300     $dbitem->itemnumber->holdingbranch($srstage->{branchcode_id})->store;
301     $dbitem->itemnumber->homebranch($srstage->{branchcode_id})->store;
302
303     # Advance again, Remove from rota.
304     ok($dbitem->advance, "Non-cyclical advance.");
305     ## Refetch dbitem
306     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
307     is($dbitem, undef, "StockRotationItem has been removed.");
308
309     $schema->storage->txn_rollback;
310 };
311
312 subtest "Tests for investigate (singular)." => sub {
313     plan tests => 7;
314     $schema->storage->txn_begin;
315
316     # Test brand new item's investigation ['initiation']
317     my $sritem = $builder->build(
318         {
319             source => 'Stockrotationitem',
320             value  => {
321                 fresh         => 1,
322                 itemnumber_id => $builder->build_sample_item->itemnumber
323             }
324         }
325     );
326     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
327     is($dbitem->investigate->{reason}, 'initiation', "fresh item initiates.");
328
329     # Test brand new item at stagebranch ['initiation']
330     $sritem = $builder->build(
331         {
332             source => 'Stockrotationitem',
333             value  => {
334                 fresh         => 1,
335                 itemnumber_id => $builder->build_sample_item->itemnumber
336             }
337         }
338     );
339     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
340     $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
341     $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
342     is($dbitem->investigate->{reason}, 'initiation', "fresh item at stagebranch initiates.");
343
344     # Test item not at stagebranch with branchtransfer history ['repatriation']
345     $sritem = $builder->build(
346         {
347             source => 'Stockrotationitem',
348             value  => {
349                 'fresh'       => 0,
350                 itemnumber_id => $builder->build_sample_item->itemnumber
351             }
352         }
353     );
354     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
355     my $dbtransfer = Koha::Item::Transfer->new({
356         'itemnumber'  => $dbitem->itemnumber_id,
357         'frombranch'  => $dbitem->itemnumber->homebranch,
358         'tobranch'    => $dbitem->itemnumber->homebranch,
359         'datesent'    => DateTime->now,
360         'datearrived' => DateTime->now,
361         'reason'      => "StockrotationAdvance",
362     })->store;
363     is($dbitem->investigate->{reason}, 'repatriation', "older item repatriates.");
364
365     # Test item at stagebranch with branchtransfer history ['not-ready']
366     $sritem = $builder->build(
367         {
368             source => 'Stockrotationitem',
369             value  => {
370                 'fresh'       => 0,
371                 itemnumber_id => $builder->build_sample_item->itemnumber
372             }
373         }
374     );
375     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
376     $dbtransfer = Koha::Item::Transfer->new({
377         'itemnumber'  => $dbitem->itemnumber_id,
378         'frombranch'  => $dbitem->itemnumber->homebranch,
379         'tobranch'    => $dbitem->stage->branchcode_id,
380         'datesent'    => DateTime->now,
381         'datearrived' => DateTime->now,
382         'reason'      => "StockrotationAdvance",
383     })->store;
384     $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
385     $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
386     is($dbitem->investigate->{reason}, 'not-ready', "older item at stagebranch not-ready.");
387
388     # Test item due for advancement ['advancement']
389     $sritem = $builder->build(
390         {
391             source => 'Stockrotationitem',
392             value  => {
393                 fresh         => 0,
394                 itemnumber_id => $builder->build_sample_item->itemnumber
395             }
396         }
397     );
398     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
399     $dbitem->indemand(0)->store;
400     $dbitem->stage->duration(50)->store;
401     my $sent_duration =  DateTime::Duration->new( days => 55);
402     my $arrived_duration =  DateTime::Duration->new( days => 52);
403     $dbtransfer = Koha::Item::Transfer->new({
404         'itemnumber'  => $dbitem->itemnumber_id,
405         'frombranch'  => $dbitem->itemnumber->homebranch,
406         'tobranch'    => $dbitem->stage->branchcode_id,
407         'datesent'    => DateTime->now - $sent_duration,
408         'datearrived' => DateTime->now - $arrived_duration,
409         'reason'      => "StockrotationAdvance",
410     })->store;
411     $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
412     $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
413     is($dbitem->investigate->{reason}, 'advancement',
414        "Item ready for advancement.");
415
416     # Test item due for advancement but in-demand ['in-demand']
417     $sritem = $builder->build(
418         {
419             source => 'Stockrotationitem',
420             value  => {
421                 fresh         => 0,
422                 itemnumber_id => $builder->build_sample_item->itemnumber
423             }
424         }
425     );
426     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
427     $dbitem->indemand(1)->store;
428     $dbitem->stage->duration(50)->store;
429     $sent_duration =  DateTime::Duration->new( days => 55);
430     $arrived_duration =  DateTime::Duration->new( days => 52);
431     $dbtransfer = Koha::Item::Transfer->new({
432         'itemnumber'  => $dbitem->itemnumber_id,
433         'frombranch'  => $dbitem->itemnumber->homebranch,
434         'tobranch'    => $dbitem->stage->branchcode_id,
435         'datesent'    => DateTime->now - $sent_duration,
436         'datearrived' => DateTime->now - $arrived_duration,
437         'reason'      => "StockrotationAdvance",
438     })->store;
439     $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
440     $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
441     is($dbitem->investigate->{reason}, 'in-demand',
442        "Item advances, but in-demand.");
443
444     # Test item ready for advancement, but at wrong library ['repatriation']
445     $sritem = $builder->build(
446         {
447             source => 'Stockrotationitem',
448             value  => {
449                 fresh         => 0,
450                 itemnumber_id => $builder->build_sample_item->itemnumber
451             }
452         }
453     );
454     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
455     $dbitem->indemand(0)->store;
456     $dbitem->stage->duration(50)->store;
457     $sent_duration =  DateTime::Duration->new( days => 55);
458     $arrived_duration =  DateTime::Duration->new( days => 52);
459     $dbtransfer = Koha::Item::Transfer->new({
460         'itemnumber'  => $dbitem->itemnumber_id,
461         'frombranch'  => $dbitem->itemnumber->homebranch,
462         'tobranch'    => $dbitem->stage->branchcode_id,
463         'datesent'    => DateTime->now - $sent_duration,
464         'datearrived' => DateTime->now - $arrived_duration,
465         'reason'      => "StockrotationAdvance",
466     })->store;
467     is($dbitem->investigate->{reason}, 'repatriation',
468        "Item advances, but not at stage branch.");
469
470     $schema->storage->txn_rollback;
471 };
472
473 1;