1 package Koha::StockRotationItem;
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::DateUtils qw( dt_from_string );
26 use Koha::Item::Transfer;
28 use Koha::StockRotationStage;
29 use Try::Tiny qw( catch try );
31 use base qw(Koha::Object);
35 StockRotationItem - Koha StockRotationItem Object class
39 StockRotationItem class used primarily by stockrotation .pls and the stock
44 Standard Koha::Objects definitions, and additional methods.
57 return 'Stockrotationitem';
62 my $item = Koha::StockRotationItem->item;
64 Returns the item associated with the current stock rotation item.
70 my $rs = $self->_result->itemnumber;
71 return Koha::Item->_new_from_dbic( $rs );
76 my $stage = Koha::StockRotationItem->stage;
78 Returns the stage associated with the current stock rotation item.
84 my $rs = $self->_result->stage;
85 return Koha::StockRotationStage->_new_from_dbic( $rs );
88 =head3 needs_repatriating
90 1|0 = $item->needs_repatriating;
92 Return 1 if this item is currently not at the library it should be at
93 according to our stockrotation plan.
97 sub needs_repatriating {
99 my ( $item, $stage ) = ( $self->item, $self->stage );
100 if ( $self->item->get_transfer ) {
101 return 0; # We're in transit.
102 } elsif ( $item->holdingbranch ne $stage->branchcode_id
103 || $item->homebranch ne $stage->branchcode_id ) {
104 return 1; # We're not where we should be.
106 return 0; # We're at home.
110 =head3 needs_advancing
112 1|0 = $item->needs_advancing;
114 Return 1 if this item is ready to be moved on to the next stage in its rota.
118 sub needs_advancing {
120 return 0 if $self->item->get_transfer; # intransfer: don't advance.
121 return 1 if $self->fresh; # Just on rota: advance.
122 my $completed = $self->item->_result->branchtransfers->search(
123 { 'reason' => "StockrotationAdvance" },
124 { order_by => { -desc => 'datearrived' } }
127 # Do maths on whether we need to be moved on.
128 if ( $completed->count ) {
129 my $arrival = dt_from_string( $completed->next->datearrived );
130 my $duration = $arrival->delta_days( dt_from_string() );
131 if ( $duration->in_units('days') >= $self->stage->duration ) {
139 warn "We have no historical branch transfer for item "
140 . $self->item->itemnumber
141 . "; This should not have happened!";
147 1|0 = $sritem->repatriate
149 Put this item into branch transfer with 'StockrotationRepatriation' comment, so
150 that it may return to it's stage.branch to continue its rota as normal.
152 Note: Stockrotation falls outside of the normal branch transfer limits and so we
153 pass 'ignore_limits' in the call to request_transfer.
158 my ( $self, $msg ) = @_;
160 # Create the transfer.
162 $self->item->request_transfer(
164 to => $self->stage->branchcode,
165 reason => "StockrotationRepatriation",
172 # Ensure the homebranch is still in sync with the rota stage
173 $self->item->homebranch( $self->stage->branchcode_id )->store;
175 return defined($transfer) ? 1 : 0;
180 1|0 = $sritem->advance;
182 Put this item into branch transfer with 'StockrotationAdvance' comment, to
183 transfer it to the next stage in its rota.
185 If this is the last stage in the rota and this rota is cyclical, we return to
186 the first stage. If it is not cyclical, then we delete this
189 If this item is 'indemand', and advance is invoked, we disable 'indemand' and
190 advance the item as per usual.
192 Note: Stockrotation falls outside of the normal branch transfer limits and so we
193 pass 'ignore_limits' in the call to request_transfer.
199 my $item = $self->item;
200 my $current_branch = $item->holdingbranch;
203 # Find and interpret our stage
204 my $stage = $self->stage;
206 if ( $self->indemand && !$self->fresh ) {
207 $self->indemand(0); # De-activate indemand
212 if ( $self->fresh ) {
213 $new_stage = $self->stage->first_sibling || $self->stage;
214 $self->fresh(0); # Reset fresh
217 elsif ( !$stage->last_sibling ) {
219 if ( $stage->rota->cyclical ) {
221 $stage->first_sibling || $stage; # Revert to first stage.
224 $self->delete; # StockRotationItem is done.
229 $new_stage = $self->stage->next_sibling; # Just advance
233 # Update stage and record transfer
234 $self->stage_id( $new_stage->stage_id ); # Set new stage
236 $item->homebranch( $new_stage->branchcode_id )->store; # Update homebranch
238 $item->request_transfer(
240 to => $new_stage->branchcode,
241 reason => "StockrotationAdvance",
242 ignore_limits => 1 # Ignore transfer limits
247 if ( $_->isa('Koha::Exceptions::Item::Transfer::InQueue') ) {
249 my $found_transfer = $_->transfer;
250 if ( $found_transfer->in_transit
251 || $found_transfer->reason eq 'Reserve'
252 || $found_transfer->reason eq 'RotatingCollection' )
254 return $item->request_transfer(
256 to => $new_stage->branchcode,
257 reason => "StockrotationAdvance",
263 return $item->request_transfer(
265 to => $new_stage->branchcode,
266 reason => "StockrotationAdvance",
270 ); # Replace transfer
277 if $item->holdingbranch eq $new_stage->branchcode_id && !$item->checkout;
278 # If item is already at branch, and not checked out
279 # If item is checked out, the return will either receive or initiate the transfer
284 =head3 toggle_indemand
286 $sritem->toggle_indemand;
288 Toggle this items in_demand status.
290 If the item is in the process of being advanced to the next stage then we cancel
291 the transfer, revert the advancement and reset the 'StockrotationAdvance' counter,
292 as though 'in_demand' had been set prior to the call to advance, by updating the
293 in progress transfer.
297 sub toggle_indemand {
300 # Toggle the item's indemand flag
301 my $new_indemand = ($self->indemand == 1) ? 0 : 1;
303 # Cancel 'StockrotationAdvance' transfer if one is in progress
305 my $item = $self->item;
306 my $transfer = $item->get_transfer;
307 if ($transfer && $transfer->reason eq 'StockrotationAdvance') {
308 my $stage = $self->stage;
310 if ( $stage->rota->cyclical && !$stage->first_sibling ) { # First stage
311 $new_stage = $stage->last_sibling;
313 $new_stage = $stage->previous_sibling;
315 $self->stage_id($new_stage->stage_id)->store; # Revert stage change
316 $item->homebranch($new_stage->branchcode_id)->store; # Revert update homebranch
317 $new_indemand = 0; # Reset indemand
318 $transfer->tobranch($new_stage->branchcode_id); # Reset StockrotationAdvance
319 $transfer->datearrived(dt_from_string); # Reset StockrotationAdvance
324 $self->indemand($new_indemand)->store;
329 my $report = $item->investigate;
331 Return the base set of information, namely this individual item's report, for
332 generating stockrotation reports about this stockrotationitem.
339 title => $self->item->_result->biblioitem
340 ->biblionumber->title,
341 author => $self->item->_result->biblioitem
342 ->biblionumber->author,
343 callnumber => $self->item->itemcallnumber,
344 location => $self->item->location,
345 onloan => $self->item->onloan,
346 barcode => $self->item->barcode,
347 itemnumber => $self->itemnumber_id,
348 branch => $self->item->_result->holdingbranch,
352 if ( $self->fresh ) {
353 $reason = 'initiation';
354 } elsif ( $self->needs_repatriating ) {
355 $reason = 'repatriation';
356 } elsif ( $self->needs_advancing ) {
357 $reason = 'advancement';
358 $reason = 'in-demand' if $self->indemand;
360 $reason = 'not-ready';
362 $item_report->{reason} = $reason;
371 Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>