View | Details | Raw Unified | Return to bug 24446
Collapse All | Expand All

(-)a/Koha/Item.pm (-7 / +18 lines)
Lines 405-421 sub holds { Link Here
405
=head3 request_transfer
405
=head3 request_transfer
406
406
407
  my $transfer = $item->request_transfer(
407
  my $transfer = $item->request_transfer(
408
      { to => $to_library, reason => $reason, ignore_limits => 0 } );
408
    {
409
        to     => $to_library,
410
        reason => $reason,
411
        [ ignore_limits => 0, enqueue => 1, replace => 1 ]
412
    }
413
  );
409
414
410
Add a transfer request for this item to the given branch for the given reason.
415
Add a transfer request for this item to the given branch for the given reason.
411
416
412
An exception will be thrown if the BranchTransferLimits would prevent the requested
417
An exception will be thrown if the BranchTransferLimits would prevent the requested
413
transfer, unless 'ignore_limits' is passed to override the limits.
418
transfer, unless 'ignore_limits' is passed to override the limits.
414
419
415
Note: At this time, only one active transfer (i.e pending arrival date) may exist
420
An exception will be thrown if an active transfer (i.e pending arrival date) is found;
416
at a time for any given item. An exception will be thrown should you attempt to
421
The caller should catch such cases and retry the transfer request as appropriate passing
417
add a request when a transfer has already been queued, whether it is in transit
422
an appropriate override.
418
or just at the request stage.
423
424
Overrides
425
* enqueue - Used to queue up the transfer when the existing transfer is found to be in transit.
426
* replace - Used to replace the existing transfer request with your own.
419
427
420
=cut
428
=cut
421
429
Lines 431-439 sub request_transfer { Link Here
431
        }
439
        }
432
    }
440
    }
433
441
434
    my $request;
442
    my $request = $self->get_transfer;
435
    Koha::Exceptions::Item::Transfer::Found->throw( transfer => $request )
443
    Koha::Exceptions::Item::Transfer::Found->throw( transfer => $request )
436
      if ( $request = $self->get_transfer );
444
      if ( $request && !$params->{enqueue} && !$params->{replace} );
445
446
    $request->cancel() if (defined($request) && $params->{replace});
437
447
438
    Koha::Exceptions::Item::Transfer::Limit->throw()
448
    Koha::Exceptions::Item::Transfer::Limit->throw()
439
      unless ( $params->{ignore_limits}
449
      unless ( $params->{ignore_limits}
Lines 449-454 sub request_transfer { Link Here
449
            comments      => $params->{comment}
459
            comments      => $params->{comment}
450
        }
460
        }
451
    )->store();
461
    )->store();
462
452
    return $transfer;
463
    return $transfer;
453
}
464
}
454
465
(-)a/Koha/StockRotationItem.pm (-10 / +49 lines)
Lines 26-31 use Koha::DateUtils qw/dt_from_string/; Link Here
26
use Koha::Item::Transfer;
26
use Koha::Item::Transfer;
27
use Koha::Item;
27
use Koha::Item;
28
use Koha::StockRotationStage;
28
use Koha::StockRotationStage;
29
use Try::Tiny;
29
30
30
use base qw(Koha::Object);
31
use base qw(Koha::Object);
31
32
Lines 143-151 sub needs_advancing { Link Here
143
144
144
  1|0 = $sritem->repatriate
145
  1|0 = $sritem->repatriate
145
146
146
Put this item into branch transfer with 'StockrotationCorrection' comment, so
147
Put this item into branch transfer with 'StockrotationRepatriation' comment, so
147
that it may return to it's stage.branch to continue its rota as normal.
148
that it may return to it's stage.branch to continue its rota as normal.
148
149
150
Note: Stockrotation falls outside of the normal branch transfer limits and so we
151
pass 'ignore_limits' in the call to request_transfer.
152
149
=cut
153
=cut
150
154
151
sub repatriate {
155
sub repatriate {
Lines 154-162 sub repatriate { Link Here
154
    # Create the transfer.
158
    # Create the transfer.
155
    my $transfer = $self->itemnumber->request_transfer(
159
    my $transfer = $self->itemnumber->request_transfer(
156
        {
160
        {
157
            to      => $self->stage->branchcode,
161
            to            => $self->stage->branchcode,
158
            reason  => "StockrotationRepatriation",
162
            reason        => "StockrotationRepatriation",
159
            comment => $msg
163
            comment       => $msg,
164
            ignore_limits => 1
160
        }
165
        }
161
    );
166
    );
162
167
Lines 180-185 StockRotationItem. Link Here
180
If this item is 'indemand', and advance is invoked, we disable 'indemand' and
185
If this item is 'indemand', and advance is invoked, we disable 'indemand' and
181
advance the item as per usual.
186
advance the item as per usual.
182
187
188
Note: Stockrotation falls outside of the normal branch transfer limits and so we
189
pass 'ignore_limits' in the call to request_transfer.
190
183
=cut
191
=cut
184
192
185
sub advance {
193
sub advance {
Lines 221-233 sub advance { Link Here
221
    # Update stage and record transfer
229
    # Update stage and record transfer
222
    $self->stage_id( $new_stage->stage_id )->store;          # Set new stage
230
    $self->stage_id( $new_stage->stage_id )->store;          # Set new stage
223
    $item->homebranch( $new_stage->branchcode_id )->store;   # Update homebranch
231
    $item->homebranch( $new_stage->branchcode_id )->store;   # Update homebranch
224
    $transfer = $item->request_transfer(
232
    $transfer = try {
225
        {
233
        $item->request_transfer(
226
            to            => $new_stage->branchcode,
234
            {
227
            reason        => "StockrotationAdvance",
235
                to            => $new_stage->branchcode,
228
            ignore_limits => 1
236
                reason        => "StockrotationAdvance",
237
                ignore_limits => 1                      # Ignore transfer limits
238
            }
239
        );                                              # Add transfer
240
    }
241
    catch {
242
        if ( $_->isa('Koha::Exceptions::Item::Transfer::Found') ) {
243
            my $exception = $_;
244
            my $found_transfer = $_->transfer;
245
            if (   $found_transfer->in_transit
246
                || $found_transfer->reason eq 'Reserve' )
247
            {
248
                return $item->request_transfer(
249
                    {
250
                        to            => $new_stage->branchcode,
251
                        reason        => "StockrotationAdvance",
252
                        ignore_limits => 1,
253
                        enqueue       => 1
254
                    }
255
                );                                      # Queue transfer
256
            } else {
257
                return $item->request_transfer(
258
                    {
259
                        to            => $new_stage->branchcode,
260
                        reason        => "StockrotationAdvance",
261
                        ignore_limits => 1,
262
                        replace       => 1
263
                    }
264
                );                                      # Replace transfer
265
            }
266
        } else {
267
            $_->rethrow();
229
        }
268
        }
230
    );                                                       # Add transfer
269
    };
231
    $transfer->receive
270
    $transfer->receive
232
      if $item->holdingbranch eq $new_stage->branchcode_id;  # Already at branch
271
      if $item->holdingbranch eq $new_stage->branchcode_id;  # Already at branch
233
272
(-)a/t/db_dependent/StockRotationItems.t (-63 / +176 lines)
Lines 24-30 use DateTime::Duration; Link Here
24
use Koha::Database;
24
use Koha::Database;
25
use Koha::DateUtils;
25
use Koha::DateUtils;
26
use Koha::Item::Transfer;
26
use Koha::Item::Transfer;
27
27
use t::lib::TestBuilder;
28
use t::lib::TestBuilder;
29
use t::lib::Mocks;
28
30
29
use Test::More tests => 8;
31
use Test::More tests => 8;
30
32
Lines 156-161 subtest "Tests for repatriate." => sub { Link Here
156
    is($intransfer->frombranch, $branch->{branchcode}, "Origin correct.");
158
    is($intransfer->frombranch, $branch->{branchcode}, "Origin correct.");
157
    is($intransfer->tobranch, $dbitem->stage->branchcode_id, "Target Correct.");
159
    is($intransfer->tobranch, $dbitem->stage->branchcode_id, "Target Correct.");
158
160
161
    # Stockrotation should overrule transfer limits
162
163
    # Stockrotation should handle transfer clashes
164
159
    $schema->storage->txn_rollback;
165
    $schema->storage->txn_rollback;
160
};
166
};
161
167
Lines 229-340 subtest "Tests for needs_advancing." => sub { Link Here
229
};
235
};
230
236
231
subtest "Tests for advance." => sub {
237
subtest "Tests for advance." => sub {
232
    plan tests => 23;
238
    plan tests => 44;
233
    $schema->storage->txn_begin;
239
    $schema->storage->txn_begin;
234
240
235
    my $sritem = $builder->build(
241
    my $sritem_1 = $builder->build_object(
236
        {
242
        {
237
            source => 'Stockrotationitem',
243
            class => 'Koha::StockRotationItems',
238
            value  => {
244
            value  => {
239
                'fresh'       => 1,
245
                'fresh'       => 1,
240
                itemnumber_id => $builder->build_sample_item->itemnumber
246
                itemnumber_id => $builder->build_sample_item->itemnumber
241
            }
247
            }
242
        }
248
        }
243
    );
249
    );
244
    my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
250
    $sritem_1->discard_changes;
245
    $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
251
    $sritem_1->itemnumber->holdingbranch($sritem_1->stage->branchcode_id);
246
    my $dbstage = $dbitem->stage;
252
    my $item_id = $sritem_1->itemnumber->itemnumber;
247
    $dbstage->position(1)->duration(50)->store; # Configure stage.
253
    my $srstage_1 = $sritem_1->stage;
254
    $srstage_1->position(1)->duration(50)->store; # Configure stage.
248
    # Configure item
255
    # Configure item
249
    $dbitem->itemnumber->holdingbranch($dbstage->branchcode_id)->store;
256
    $sritem_1->itemnumber->holdingbranch($srstage_1->branchcode_id)->store;
250
    $dbitem->itemnumber->homebranch($dbstage->branchcode_id)->store;
257
    $sritem_1->itemnumber->homebranch($srstage_1->branchcode_id)->store;
251
    # Sanity check
258
    # Sanity check
252
    is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage sanity check.");
259
    is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Stage sanity check.");
253
260
254
    # Test if an item is fresh, always move to first stage.
261
    # Test if an item is fresh, always move to first stage.
255
    is($dbitem->fresh, 1, "Fresh is correct.");
262
    is($sritem_1->fresh, 1, "Fresh is correct.");
256
    $dbitem->advance;
263
    $sritem_1->advance;
257
    is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage is first stage after fresh advance.");
264
    is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Stage is first stage after fresh advance.");
258
    is($dbitem->fresh, 0, "Fresh reset after advance.");
265
    is($sritem_1->fresh, 0, "Fresh reset after advance.");
259
266
260
    # Test cases of single stage
267
    # Test cases of single stage
261
    $dbstage->rota->cyclical(1)->store;         # Set Rota to cyclical.
268
    $srstage_1->rota->cyclical(1)->store;         # Set Rota to cyclical.
262
    ok($dbitem->advance, "Single stage cyclical advance done.");
269
    ok($sritem_1->advance, "Single stage cyclical advance done.");
263
    ## Refetch dbitem
270
    ## Refetch sritem_1
264
    $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
271
    $sritem_1->discard_changes;
265
    is($dbitem->stage->stage_id, $dbstage->stage_id, "Single stage cyclical stage OK.");
272
    is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Single stage cyclical stage OK.");
266
273
267
    # Test with indemand advance
274
    # Test with indemand advance
268
    $dbitem->indemand(1)->store;
275
    $sritem_1->indemand(1)->store;
269
    ok($dbitem->advance, "Indemand item advance done.");
276
    ok($sritem_1->advance, "Indemand item advance done.");
270
    ## Refetch dbitem
277
    ## Refetch sritem_1
271
    $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
278
    $sritem_1->discard_changes;
272
    is($dbitem->indemand, 0, "Indemand OK.");
279
    is($sritem_1->indemand, 0, "Indemand OK.");
273
    is($dbitem->stage->stage_id, $dbstage->stage_id, "Indemand item advance stage OK.");
280
    is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Indemand item advance stage OK.");
274
281
275
    # Multi stages
282
    # Multi stages
276
    my $srstage = $builder->build({
283
    my $srstage_2 = $builder->build_object({
277
        source => 'Stockrotationstage',
284
        class => 'Koha::StockRotationStages',
278
        value => { duration => 50 }
285
        value => { duration => 50 }
279
    });
286
    });
280
    my $dbstage2 = Koha::StockRotationStages->find($srstage->{stage_id});
287
    $srstage_2->discard_changes;
281
    $dbstage2->move_to_group($dbitem->stage->rota_id);
288
    $srstage_2->move_to_group($sritem_1->stage->rota_id);
282
    $dbstage2->move_last;
289
    $srstage_2->move_last;
283
290
284
    # Test a straight up advance
291
    # Test a straight up advance
285
    ok($dbitem->advance, "Advancement done.");
292
    ok($sritem_1->advance, "Advancement done.");
286
    ## Refetch dbitem
293
    ## Refetch sritem_1
287
    $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
294
    $sritem_1->discard_changes;
288
    ## Test results
295
    ## Test results
289
    is($dbitem->stage->stage_id, $dbstage2->stage_id, "Stage updated.");
296
    is($sritem_1->stage->stage_id, $srstage_2->stage_id, "Stage updated.");
290
    is(
297
    is(
291
        $dbitem->itemnumber->homebranch,
298
        $sritem_1->itemnumber->homebranch,
292
        $dbstage2->branchcode_id,
299
        $srstage_2->branchcode_id,
293
        "Item homebranch updated"
300
        "Item homebranch updated"
294
    );
301
    );
295
    my $intransfer = $dbitem->itemnumber->get_transfer;
302
    my $transfer_request = $sritem_1->itemnumber->get_transfer;
296
    is($intransfer->frombranch, $dbstage->branchcode_id, "Origin correct.");
303
    is($transfer_request->frombranch, $srstage_1->branchcode_id, "Origin correct.");
297
    is($intransfer->tobranch, $dbstage2->branchcode_id, "Target Correct.");
304
    is($transfer_request->tobranch, $srstage_2->branchcode_id, "Target Correct.");
305
    is($transfer_request->datesent, undef, "Transfer requested, but not sent.");
298
306
299
    # Arrive at new branch
307
    # Arrive at new branch
300
    $intransfer->datearrived(dt_from_string())->store;
308
    $transfer_request->datearrived(dt_from_string())->store;
301
    $dbitem->itemnumber->holdingbranch($srstage->{branchcode_id})->store;
309
    $sritem_1->itemnumber->holdingbranch($srstage_2->branchcode_id)->store;
302
310
303
    # Test a cyclical advance
311
    # Test a cyclical advance
304
    ok($dbitem->advance, "Cyclical advancement done.");
312
    ok($sritem_1->advance, "Cyclical advancement done.");
305
    ## Refetch dbitem
313
    ## Refetch sritem_1
306
    $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
314
    $sritem_1->discard_changes;
307
    ## Test results
315
    ## Test results
308
    is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage updated.");
316
    is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Stage updated.");
309
    is(
317
    is(
310
        $dbitem->itemnumber->homebranch,
318
        $sritem_1->itemnumber->homebranch,
311
        $dbstage->branchcode_id,
319
        $srstage_1->branchcode_id,
312
        "Item homebranch updated"
320
        "Item homebranch updated"
313
    );
321
    );
314
    $intransfer = $dbitem->itemnumber->get_transfer;
322
    $transfer_request = $sritem_1->itemnumber->get_transfer;
315
    is($intransfer->frombranch, $dbstage2->branchcode_id, "Origin correct.");
323
    is($transfer_request->frombranch, $srstage_2->branchcode_id, "Origin correct.");
316
    is($intransfer->tobranch, $dbstage->branchcode_id, "Target correct.");
324
    is($transfer_request->tobranch, $srstage_1->branchcode_id, "Target correct.");
317
325
318
    # Arrive at new branch
326
    # Arrive at new branch
319
    $intransfer->datearrived(dt_from_string())->store;
327
    $transfer_request->datearrived(dt_from_string())->store;
320
    $dbitem->itemnumber->holdingbranch($srstage->{branchcode_id})->store;
328
    $sritem_1->itemnumber->holdingbranch($srstage_1->branchcode_id)->store;
329
330
    # Confirm that stockrotation ignores transfer limits
331
    t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1);
332
    t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype');
333
    my $limit = Koha::Item::Transfer::Limit->new(
334
        {
335
            fromBranch => $srstage_1->branchcode_id,
336
            toBranch   => $srstage_2->branchcode_id,
337
            itemtype   => $sritem_1->itemnumber->effective_itemtype,
338
        }
339
    )->store;
321
340
322
    $dbstage->rota->cyclical(0)->store;         # Set Rota to non-cyclical.
341
    ok($sritem_1->advance, "Advancement overrules transfer limits.");
342
    ## Refetch sritem_1
343
    $sritem_1->discard_changes;
344
    ## Test results
345
    is($sritem_1->stage->stage_id, $srstage_2->stage_id, "Stage updated ignoring transfer limits.");
346
    is(
347
        $sritem_1->itemnumber->homebranch,
348
        $srstage_2->branchcode_id,
349
        "Item homebranch updated ignoring transfer limits"
350
    );
351
    $transfer_request = $sritem_1->itemnumber->get_transfer;
352
    is($transfer_request->frombranch, $srstage_1->branchcode_id, "Origin correct ignoring transfer limits.");
353
    is($transfer_request->tobranch, $srstage_2->branchcode_id, "Target correct ignoring transfer limits.");
354
355
    # Arrive at new branch
356
    $transfer_request->datearrived(dt_from_string())->store;
357
    $sritem_1->itemnumber->holdingbranch($srstage_2->branchcode_id)->store;
358
   
359
    # Setup a conflicting manual transfer
360
    my $item = Koha::Items->find($item_id);
361
    $item->request_transfer({ to => $srstage_1->branchcode, reason => "Manual" });
362
    $transfer_request = $item->get_transfer;
363
    is (ref($transfer_request), 'Koha::Item::Transfer', "Conflicting transfer added");
364
    is ($transfer_request->reason, 'Manual', "Conflicting transfer reason is 'Manual'");
365
366
    # Advance item whilst conflicting manual transfer exists
367
    ok($sritem_1->advance, "Advancement done.");
368
    ## Refetch sritem_1
369
    $sritem_1->discard_changes;
370
371
    ## Refetch conflicted transfer
372
    $transfer_request->discard_changes;
373
374
    # Conflicted transfer should have been cancelled
375
    isnt($transfer_request->datecancelled, undef, "Conflicting manual transfer was cancelled");
376
377
    # StockRotationAdvance transfer added
378
    $transfer_request = $sritem_1->itemnumber->get_transfer;
379
    is($transfer_request->reason, 'StockrotationAdvance', "StockrotationAdvance transfer added");
380
    is($transfer_request->frombranch, $srstage_2->branchcode_id, "Origin correct.");
381
    is($transfer_request->tobranch, $srstage_1->branchcode_id, "Target correct.");
382
383
    # Arrive at new branch
384
    $transfer_request->datearrived(dt_from_string())->store;
385
    $sritem_1->itemnumber->holdingbranch($srstage_1->branchcode_id)->store;
386
387
    # Setup a conflicting reserve transfer
388
    $item->request_transfer({ to => $srstage_2->branchcode, reason => "Reserve" });
389
    $transfer_request = $item->get_transfer;
390
    is (ref($transfer_request), 'Koha::Item::Transfer', "Conflicting transfer added");
391
    is ($transfer_request->reason, 'Reserve', "Conflicting transfer reason is 'Reserve'");
392
393
    # Advance item whilst conflicting reserve transfer exists
394
    ok($sritem_1->advance, "Advancement done.");
395
    ## Refetch sritem_1
396
    $sritem_1->discard_changes;
397
398
    ## Refetch conflicted transfer
399
    $transfer_request->discard_changes;
400
401
    # Conflicted transfer should not been cancelled
402
    is($transfer_request->datecancelled, undef, "Conflicting reserve transfer was not cancelled");
403
404
    # StockRotationAdvance transfer added
405
    my $transfer_requests = Koha::Item::Transfers->search(
406
        {
407
            itemnumber    => $sritem_1->itemnumber->itemnumber,
408
            datearrived   => undef,
409
            datecancelled => undef
410
        }
411
    );
412
    is($transfer_requests->count, '2', "StockrotationAdvance transfer queued");
413
414
    # Arrive at new branch
415
    $transfer_request->datearrived(dt_from_string())->store;
416
    $sritem_1->itemnumber->holdingbranch($srstage_2->branchcode_id)->store;
417
418
    # StockRotationAdvance transfer added
419
    $transfer_request = $sritem_1->itemnumber->get_transfer;
420
    is($transfer_request->reason, 'StockrotationAdvance', "StockrotationAdvance transfer remains after reserve is met");
421
    is($transfer_request->frombranch, $srstage_1->branchcode_id, "Origin correct.");
422
    is($transfer_request->tobranch, $srstage_2->branchcode_id, "Target correct.");
423
    
424
    # Arrive at new branch
425
    $transfer_request->datearrived(dt_from_string())->store;
426
    $sritem_1->itemnumber->holdingbranch($srstage_2->branchcode_id)->store;
427
428
    $srstage_1->rota->cyclical(0)->store;         # Set Rota to non-cyclical.
429
430
    my $srstage_3 = $builder->build_object({
431
        class => 'Koha::StockRotationStages',
432
        value => { duration => 50 }
433
    });
434
    $srstage_3->discard_changes;
435
    $srstage_3->move_to_group($sritem_1->stage->rota_id);
436
    $srstage_3->move_last;
323
437
324
    # Advance again, to end of rota.
438
    # Advance again, to end of rota.
325
    ok($dbitem->advance, "Non-cyclical advance to last stage.");
439
    ok($sritem_1->advance, "Non-cyclical advance to last stage.");
326
440
327
    # Arrive at new branch
441
    # Arrive at new branch
328
    $intransfer->datearrived(dt_from_string())->store;
442
    $transfer_request->datearrived(dt_from_string())->store;
329
    $dbitem->itemnumber->holdingbranch($srstage->{branchcode_id})->store;
443
    $sritem_1->itemnumber->holdingbranch($srstage_3->branchcode_id)->store;
330
444
331
    # Advance again, Remove from rota.
445
    # Advance again, Remove from rota.
332
    ok($dbitem->advance, "Non-cyclical advance.");
446
    ok($sritem_1->advance, "Non-cyclical advance.");
333
    ## Refetch dbitem
447
    ## Refetch sritem_1
334
    $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
448
    $sritem_1 = Koha::StockRotationItems->find({ itemnumber_id => $item_id });
335
    is($dbitem, undef, "StockRotationItem has been removed.");
449
    is($sritem_1, undef, "StockRotationItem has been removed.");
336
    my $item = Koha::Items->find($sritem->{itemnumber_id});
450
    $item = Koha::Items->find($item_id);
337
    is($item->homebranch, $srstage->{branchcode_id}, "Item homebranch remains");
451
    is($item->homebranch, $srstage_3->branchcode_id, "Item homebranch remains");
338
452
339
    $schema->storage->txn_rollback;
453
    $schema->storage->txn_rollback;
340
};
454
};
341
- 

Return to bug 24446