Bug 24446: Improve StockRotationItem->advance tests
[koha-ffzg.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;
26 use Koha::Item::Transfer;
27 use t::lib::TestBuilder;
28 use Test::Warn;
29
30 use Test::More tests => 8;
31
32 my $schema = Koha::Database->new->schema;
33
34 use_ok('Koha::StockRotationItems');
35 use_ok('Koha::StockRotationItem');
36
37 my $builder = t::lib::TestBuilder->new;
38
39 subtest 'Basic object tests' => sub {
40
41     plan tests => 5;
42
43     $schema->storage->txn_begin;
44
45     my $itm = $builder->build_sample_item;
46     my $stage = $builder->build({ source => 'Stockrotationstage' });
47
48     my $item = $builder->build({
49         source => 'Stockrotationitem',
50         value  => {
51             itemnumber_id => $itm->itemnumber,
52             stage_id      => $stage->{stage_id},
53         },
54     });
55
56     my $sritem = Koha::StockRotationItems->find($item->{itemnumber_id});
57     isa_ok(
58         $sritem,
59         'Koha::StockRotationItem',
60         "Correctly create and load a stock rotation item."
61     );
62
63     # Relationship to rota
64     isa_ok( $sritem->itemnumber, 'Koha::Item', "Fetched related item." );
65     is( $sritem->itemnumber->itemnumber, $itm->itemnumber, "Related rota OK." );
66
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." );
70
71
72     $schema->storage->txn_rollback;
73 };
74
75 subtest 'Tests for needs_repatriating' => sub {
76
77     plan tests => 4;
78
79     $schema->storage->txn_begin;
80
81     # Setup a pristine stockrotation context.
82     my $sritem = $builder->build(
83         {
84             source => 'Stockrotationitem',
85             value =>
86               { itemnumber_id => $builder->build_sample_item->itemnumber }
87         }
88     );
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);
93
94     my $dbrota = $dbitem->stage->rota;
95     my $newstage = $builder->build({
96         source => 'Stockrotationstage',
97         value => {
98             rota_id => $dbrota->rota_id,
99             position => 2,
100         }
101     });
102
103     # - homebranch == holdingbranch [0]
104     is(
105         $dbitem->needs_repatriating, 0,
106         "Homebranch == Holdingbranch."
107     );
108
109     my $branch = $builder->build({ source => 'Branch' });
110     $dbitem->itemnumber->holdingbranch($branch->{branchcode});
111
112     # - homebranch != holdingbranch [1]
113     is(
114         $dbitem->needs_repatriating, 1,
115         "Homebranch != holdingbranch."
116     );
117
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]
122     is(
123         $dbitem->needs_repatriating, 1,
124         "Homebranch != StockRotationStage.Branchcode_id & not in transit."
125     );
126
127     # Set to in transit (by implication).
128     $dbitem->stage($newstage->{stage_id});
129     # - homebranch != stockrotaitonstage.branch & in transit [0]
130     is(
131         $dbitem->needs_repatriating, 1,
132         "homebranch != stockrotaitonstage.branch & in transit."
133     );
134
135     $schema->storage->txn_rollback;
136 };
137
138 subtest "Tests for repatriate." => sub {
139     plan tests => 3;
140     $schema->storage->txn_begin;
141     my $sritem = $builder->build(
142         {
143             source => 'Stockrotationitem',
144             value =>
145               { itemnumber_id => $builder->build_sample_item->itemnumber }
146         }
147     );
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});
153
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.");
159
160     $schema->storage->txn_rollback;
161 };
162
163 subtest "Tests for needs_advancing." => sub {
164     plan tests => 7;
165     $schema->storage->txn_begin;
166
167     # Test behaviour of item freshly added to rota.
168     my $sritem = $builder->build(
169         {
170             source => 'Stockrotationitem',
171             value  => {
172                 'fresh'       => 1,
173                 itemnumber_id => $builder->build_sample_item->itemnumber
174             },
175         }
176     );
177     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
178     is($dbitem->needs_advancing, 1, "An item that is fresh will always need advancing.");
179
180     # Setup a pristine stockrotation context.
181     $sritem = $builder->build(
182         {
183             source => 'Stockrotationitem',
184             value  => {
185                 'fresh'       => 0,
186                 itemnumber_id => $builder->build_sample_item->itemnumber
187             }
188         }
189     );
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);
195
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",
203     })->store;
204
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;
211
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;
219
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 )
223     )->store;
224     $dbtransfer->datearrived(   # And arrived 75 days ago.
225         dt_from_string() - DateTime::Duration->new( days => 75 )
226     )->store;
227     is($dbitem->needs_advancing, 1, "Ready to be advanced.");
228     $dbtransfer->delete;
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.";
230
231     $schema->storage->txn_rollback;
232 };
233
234 subtest "Tests for advance." => sub {
235     plan tests => 23;
236     $schema->storage->txn_begin;
237
238     my $sritem = $builder->build(
239         {
240             source => 'Stockrotationitem',
241             value  => {
242                 'fresh'       => 1,
243                 itemnumber_id => $builder->build_sample_item->itemnumber
244             }
245         }
246     );
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.
251     # Configure item
252     $dbitem->itemnumber->holdingbranch($dbstage->branchcode_id)->store;
253     $dbitem->itemnumber->homebranch($dbstage->branchcode_id)->store;
254     # Sanity check
255     is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage sanity check.");
256
257     # Test if an item is fresh, always move to first stage.
258     is($dbitem->fresh, 1, "Fresh is correct.");
259     $dbitem->advance;
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.");
262
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.");
266     ## Refetch dbitem
267     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
268     is($dbitem->stage->stage_id, $dbstage->stage_id, "Single stage cyclical stage OK.");
269
270     # Test with indemand advance
271     $dbitem->indemand(1)->store;
272     ok($dbitem->advance, "Indemand item advance done.");
273     ## Refetch dbitem
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.");
277
278     # Multi stages
279     my $srstage = $builder->build({
280         source => 'Stockrotationstage',
281         value => { duration => 50 }
282     });
283     my $dbstage2 = Koha::StockRotationStages->find($srstage->{stage_id});
284     $dbstage2->move_to_group($dbitem->stage->rota_id);
285     $dbstage2->move_last;
286
287     # Test a straight up advance
288     ok($dbitem->advance, "Advancement done.");
289     ## Refetch dbitem
290     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
291     ## Test results
292     is($dbitem->stage->stage_id, $dbstage2->stage_id, "Stage updated.");
293     is(
294         $dbitem->itemnumber->homebranch,
295         $dbstage2->branchcode_id,
296         "Item homebranch updated"
297     );
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.");
301
302     # Arrive at new branch
303     $intransfer->datearrived(dt_from_string())->store;
304     $dbitem->itemnumber->holdingbranch($srstage->{branchcode_id})->store;
305
306     # Test a cyclical advance
307     ok($dbitem->advance, "Cyclical advancement done.");
308     ## Refetch dbitem
309     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
310     ## Test results
311     is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage updated.");
312     is(
313         $dbitem->itemnumber->homebranch,
314         $dbstage->branchcode_id,
315         "Item homebranch updated"
316     );
317     $intransfer = $dbitem->itemnumber->get_transfer;
318     is($intransfer->frombranch, $dbstage2->branchcode_id, "Origin correct.");
319     is($intransfer->tobranch, $dbstage->branchcode_id, "Target correct.");
320
321     # Arrive at new branch
322     $intransfer->datearrived(dt_from_string())->store;
323     $dbitem->itemnumber->holdingbranch($srstage->{branchcode_id})->store;
324
325     $dbstage->rota->cyclical(0)->store;         # Set Rota to non-cyclical.
326
327     # Advance again, to end of rota.
328     ok($dbitem->advance, "Non-cyclical advance to last stage.");
329
330     # Arrive at new branch
331     $intransfer->datearrived(dt_from_string())->store;
332     $dbitem->itemnumber->holdingbranch($srstage->{branchcode_id})->store;
333
334     # Advance again, Remove from rota.
335     ok($dbitem->advance, "Non-cyclical advance.");
336     ## Refetch dbitem
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");
341
342     $schema->storage->txn_rollback;
343 };
344
345 subtest "Tests for investigate (singular)." => sub {
346     plan tests => 7;
347     $schema->storage->txn_begin;
348
349     # Test brand new item's investigation ['initiation']
350     my $sritem = $builder->build(
351         {
352             source => 'Stockrotationitem',
353             value  => {
354                 fresh         => 1,
355                 itemnumber_id => $builder->build_sample_item->itemnumber
356             }
357         }
358     );
359     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
360     is($dbitem->investigate->{reason}, 'initiation', "fresh item initiates.");
361
362     # Test brand new item at stagebranch ['initiation']
363     $sritem = $builder->build(
364         {
365             source => 'Stockrotationitem',
366             value  => {
367                 fresh         => 1,
368                 itemnumber_id => $builder->build_sample_item->itemnumber
369             }
370         }
371     );
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.");
376
377     # Test item not at stagebranch with branchtransfer history ['repatriation']
378     $sritem = $builder->build(
379         {
380             source => 'Stockrotationitem',
381             value  => {
382                 'fresh'       => 0,
383                 itemnumber_id => $builder->build_sample_item->itemnumber
384             }
385         }
386     );
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",
395     })->store;
396     is($dbitem->investigate->{reason}, 'repatriation', "older item repatriates.");
397
398     # Test item at stagebranch with branchtransfer history ['not-ready']
399     $sritem = $builder->build(
400         {
401             source => 'Stockrotationitem',
402             value  => {
403                 'fresh'       => 0,
404                 itemnumber_id => $builder->build_sample_item->itemnumber
405             }
406         }
407     );
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",
416     })->store;
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.");
420
421     # Test item due for advancement ['advancement']
422     $sritem = $builder->build(
423         {
424             source => 'Stockrotationitem',
425             value  => {
426                 fresh         => 0,
427                 itemnumber_id => $builder->build_sample_item->itemnumber
428             }
429         }
430     );
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",
443     })->store;
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.");
448
449     # Test item due for advancement but in-demand ['in-demand']
450     $sritem = $builder->build(
451         {
452             source => 'Stockrotationitem',
453             value  => {
454                 fresh         => 0,
455                 itemnumber_id => $builder->build_sample_item->itemnumber
456             }
457         }
458     );
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",
471     })->store;
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.");
476
477     # Test item ready for advancement, but at wrong library ['repatriation']
478     $sritem = $builder->build(
479         {
480             source => 'Stockrotationitem',
481             value  => {
482                 fresh         => 0,
483                 itemnumber_id => $builder->build_sample_item->itemnumber
484             }
485         }
486     );
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",
499     })->store;
500     is($dbitem->investigate->{reason}, 'repatriation',
501        "Item advances, but not at stage branch.");
502
503     $schema->storage->txn_rollback;
504 };
505
506 1;