Bug 26265: (QA follow-up) Remove g option from regex, add few dirs
[koha.git] / Koha / StockRotationItem.pm
1 package Koha::StockRotationItem;
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 use Koha::Item;
28 use Koha::StockRotationStage;
29
30 use base qw(Koha::Object);
31
32 =head1 NAME
33
34 StockRotationItem - Koha StockRotationItem Object class
35
36 =head1 SYNOPSIS
37
38 StockRotationItem class used primarily by stockrotation .pls and the stock
39 rotation cron script.
40
41 =head1 DESCRIPTION
42
43 Standard Koha::Objects definitions, and additional methods.
44
45 =head1 API
46
47 =head2 Class Methods
48
49 =cut
50
51 =head3 _type
52
53 =cut
54
55 sub _type {
56     return 'Stockrotationitem';
57 }
58
59 =head3 itemnumber
60
61   my $item = Koha::StockRotationItem->itemnumber;
62
63 Returns the item associated with the current stock rotation item.
64
65 =cut
66
67 sub itemnumber {
68     my ( $self ) = @_;
69     my $rs = $self->_result->itemnumber;
70     return Koha::Item->_new_from_dbic( $rs );
71 }
72
73 =head3 stage
74
75   my $stage = Koha::StockRotationItem->stage;
76
77 Returns the stage associated with the current stock rotation item.
78
79 =cut
80
81 sub stage {
82     my ( $self ) = @_;
83     my $rs = $self->_result->stage;
84     return Koha::StockRotationStage->_new_from_dbic( $rs );
85 }
86
87 =head3 needs_repatriating
88
89   1|0 = $item->needs_repatriating;
90
91 Return 1 if this item is currently not at the library it should be at
92 according to our stockrotation plan.
93
94 =cut
95
96 sub needs_repatriating {
97     my ( $self ) = @_;
98     my ( $item, $stage ) = ( $self->itemnumber, $self->stage );
99     if ( $self->itemnumber->get_transfer ) {
100         return 0;               # We're in transit.
101     } elsif ( $item->holdingbranch ne $stage->branchcode_id
102                   || $item->homebranch ne $stage->branchcode_id ) {
103         return 1;               # We're not where we should be.
104     } else {
105         return 0;               # We're at home.
106     }
107 }
108
109 =head3 needs_advancing
110
111   1|0 = $item->needs_advancing;
112
113 Return 1 if this item is ready to be moved on to the next stage in its rota.
114
115 =cut
116
117 sub needs_advancing {
118     my ( $self ) = @_;
119     return 0 if $self->itemnumber->get_transfer; # intransfer: don't advance.
120     return 1 if $self->fresh;                    # Just on rota: advance.
121     my $completed = $self->itemnumber->_result->branchtransfers->search(
122         { 'reason' => "StockrotationAdvance" },
123         { order_by => { -desc => 'datearrived' } }
124     );
125     # Do maths on whether we need to be moved on.
126     if ( $completed->count ) {
127         my $arrival = dt_from_string(
128             $completed->next->datearrived, 'iso'
129         );
130         my $duration = DateTime::Duration
131             ->new( days => $self->stage->duration );
132         if ( $arrival + $duration le dt_from_string() ) {
133             return 1;
134         } else {
135             return 0;
136         }
137     } else {
138         die "We have no historical branch transfer; this should not have happened!";
139     }
140 }
141
142 =head3 repatriate
143
144   1|0 = $sritem->repatriate
145
146 Put this item into branch transfer with 'StockrotationCorrection' comment, so
147 that it may return to it's stage.branch to continue its rota as normal.
148
149 =cut
150
151 sub repatriate {
152     my ( $self, $msg ) = @_;
153     # Create the transfer.
154     my $transfer_stored = Koha::Item::Transfer->new({
155         'itemnumber' => $self->itemnumber_id,
156         'frombranch' => $self->itemnumber->holdingbranch,
157         'tobranch'   => $self->stage->branchcode_id,
158         'datesent'   => dt_from_string(),
159         'comments'   => $msg,
160         'reason'     => "StockrotationRepatriation"
161     })->store;
162     $self->itemnumber->homebranch($self->stage->branchcode_id)->store;
163     return $transfer_stored;
164 }
165
166 =head3 advance
167
168   1|0 = $sritem->advance;
169
170 Put this item into branch transfer with 'StockrotationAdvance' comment, to
171 transfer it to the next stage in its rota.
172
173 If this is the last stage in the rota and this rota is cyclical, we return to
174 the first stage.  If it is not cyclical, then we delete this
175 StockRotationItem.
176
177 If this item is 'indemand', and advance is invoked, we disable 'indemand' and
178 advance the item as per usual.
179
180 =cut
181
182 sub advance {
183     my ( $self ) = @_;
184     my $item = $self->itemnumber;
185     my $transfer = Koha::Item::Transfer->new({
186         'itemnumber' => $self->itemnumber_id,
187         'frombranch' => $item->holdingbranch,
188         'datesent'   => dt_from_string(),
189         'reason'     => "StockrotationAdvance"
190     });
191
192     if ( $self->indemand && !$self->fresh ) {
193         $self->indemand(0)->store;  # De-activate indemand
194         $transfer->tobranch($self->stage->branchcode_id);
195         $transfer->datearrived(dt_from_string());
196     } else {
197         # Find and update our stage.
198         my $stage = $self->stage;
199         my $new_stage;
200         if ( $self->fresh ) {   # Just added to rota
201             $new_stage = $self->stage->first_sibling || $self->stage;
202             $transfer->tobranch($new_stage->branchcode_id);
203             $transfer->datearrived(dt_from_string()) # Already at first branch
204                 if $item->holdingbranch eq $new_stage->branchcode_id;
205             $self->fresh(0)->store;         # Reset fresh
206         } elsif ( !$stage->last_sibling ) { # Last stage
207             if ( $stage->rota->cyclical ) { # Cyclical rota?
208                 # Revert to first stage.
209                 $new_stage = $stage->first_sibling || $stage;
210                 $transfer->tobranch($new_stage->branchcode_id);
211                 $transfer->datearrived(dt_from_string());
212             } else {
213                 $self->delete;  # StockRotationItem is done.
214                 return 1;
215             }
216         } else {
217             # Just advance.
218             $new_stage = $self->stage->next_sibling;
219         }
220         $self->stage_id($new_stage->stage_id)->store;        # Set new stage
221         $item->homebranch($new_stage->branchcode_id)->store; # Update homebranch
222         $transfer->tobranch($new_stage->branchcode_id);      # Send to new branch
223     }
224
225     return $transfer->store;
226 }
227
228 =head3 investigate
229
230   my $report = $item->investigate;
231
232 Return the base set of information, namely this individual item's report, for
233 generating stockrotation reports about this stockrotationitem.
234
235 =cut
236
237 sub investigate {
238     my ( $self ) = @_;
239     my $item_report = {
240         title      => $self->itemnumber->_result->biblioitem
241             ->biblionumber->title,
242         author     => $self->itemnumber->_result->biblioitem
243             ->biblionumber->author,
244         callnumber => $self->itemnumber->itemcallnumber,
245         location   => $self->itemnumber->location,
246         onloan     => $self->itemnumber->onloan,
247         barcode    => $self->itemnumber->barcode,
248         itemnumber => $self->itemnumber_id,
249         branch => $self->itemnumber->_result->holdingbranch,
250         object => $self,
251     };
252     my $reason;
253     if ( $self->fresh ) {
254         $reason = 'initiation';
255     } elsif ( $self->needs_repatriating ) {
256         $reason = 'repatriation';
257     } elsif ( $self->needs_advancing ) {
258         $reason = 'advancement';
259         $reason = 'in-demand' if $self->indemand;
260     } else {
261         $reason = 'not-ready';
262     }
263     $item_report->{reason} = $reason;
264
265     return $item_report;
266 }
267
268 1;
269
270 =head1 AUTHOR
271
272 Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
273
274 =cut