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

(-)a/Koha/Exceptions.pm (+5 lines)
Lines 15-20 use Exception::Class ( Link Here
15
        isa => 'Koha::Exception',
15
        isa => 'Koha::Exception',
16
        description => 'Same object already exists',
16
        description => 'Same object already exists',
17
    },
17
    },
18
    'Koha::Exceptions::InvalidStatus' => {
19
        isa         => 'Koha::Exception',
20
        description => 'The current status is not valid in context',
21
        fields      => ['invalid_status'],
22
    },
18
    'Koha::Exceptions::ObjectNotFound' => {
23
    'Koha::Exceptions::ObjectNotFound' => {
19
        isa => 'Koha::Exception',
24
        isa => 'Koha::Exception',
20
        description => 'The required object doesn\'t exist',
25
        description => 'The required object doesn\'t exist',
(-)a/Koha/Hold.pm (+89 lines)
Lines 30-35 use Koha::AuthorisedValues; Link Here
30
use Koha::DateUtils qw( dt_from_string );
30
use Koha::DateUtils qw( dt_from_string );
31
use Koha::Patrons;
31
use Koha::Patrons;
32
use Koha::Biblios;
32
use Koha::Biblios;
33
use Koha::Hold::CancellationRequests;
33
use Koha::Items;
34
use Koha::Items;
34
use Koha::Libraries;
35
use Koha::Libraries;
35
use Koha::Old::Holds;
36
use Koha::Old::Holds;
Lines 38-43 use Koha::Plugins; Link Here
38
39
39
use Koha::BackgroundJob::BatchUpdateBiblioHoldsQueue;
40
use Koha::BackgroundJob::BatchUpdateBiblioHoldsQueue;
40
41
42
use Koha::Exceptions;
41
use Koha::Exceptions::Hold;
43
use Koha::Exceptions::Hold;
42
44
43
use base qw(Koha::Object);
45
use base qw(Koha::Object);
Lines 416-421 sub is_cancelable_from_opac { Link Here
416
    return 0; # if ->is_in_transit or if ->is_waiting or ->is_in_processing
418
    return 0; # if ->is_in_transit or if ->is_waiting or ->is_in_processing
417
}
419
}
418
420
421
=head3 cancellation_requestable_from_opac
422
423
    if ( $hold->cancellation_requestable_from_opac ) { ... }
424
425
Returns a I<boolean> representing if a cancellation request can be placed on the hold
426
from the OPAC. It targets holds that cannot be cancelled from the OPAC (see the
427
B<is_cancelable_from_opac> method above), but for which circulation rules allow
428
requesting cancellation.
429
430
Throws a B<Koha::Exceptions::InvalidStatus> exception with the following I<invalid_status>
431
values:
432
433
=over 4
434
435
=item B<'hold_not_waiting'>: the hold is expected to be waiting and it is not.
436
437
=item B<'no_item_linked'>: the waiting hold doesn't have an item properly linked.
438
439
=back
440
441
=cut
442
443
sub cancellation_requestable_from_opac {
444
    my ( $self ) = @_;
445
446
    Koha::Exceptions::InvalidStatus->throw( invalid_status => 'hold_not_waiting' )
447
      unless $self->is_waiting;
448
449
    my $item = $self->item;
450
451
    Koha::Exceptions::InvalidStatus->throw( invalid_status => 'no_item_linked' )
452
      unless $item;
453
454
    my $patron = $self->patron;
455
456
    my $controlbranch = $patron->branchcode;
457
458
    if ( C4::Context->preference('ReservesControlBranch') eq 'ItemHomeLibrary' ) {
459
        $controlbranch = $item->homebranch;
460
    }
461
462
    return Koha::CirculationRules->get_effective_rule(
463
        {
464
            categorycode => $patron->categorycode,
465
            itemtype     => $item->itype,
466
            branchcode   => $controlbranch,
467
            rule_name    => 'waiting_hold_cancellation',
468
        }
469
    )->rule_value ? 1 : 0;
470
}
471
419
=head3 is_at_destination
472
=head3 is_at_destination
420
473
421
Returns true if hold is waiting
474
Returns true if hold is waiting
Lines 525-530 sub is_suspended { Link Here
525
    return $self->suspend();
578
    return $self->suspend();
526
}
579
}
527
580
581
=head3 add_cancellation_request
582
583
    my $cancellation_request = $hold->add_cancellation_request({ [ creation_date => $creation_date ] });
584
585
Adds a cancellation request to the hold. Returns the generated
586
I<Koha::Hold::CancellationRequest> object.
587
588
=cut
589
590
sub add_cancellation_request {
591
    my ( $self, $params ) = @_;
592
593
    my $request = Koha::Hold::CancellationRequest->new(
594
        {   hold_id      => $self->id,
595
            ( $params->{creation_date} ? ( creation_date => $params->{creation_date} ) : () ),
596
        }
597
    )->store;
598
599
    $request->discard_changes;
600
601
    return $request;
602
}
603
604
=head3 cancellation_requests
605
606
    my $cancellation_requests = $hold->cancellation_requests;
607
608
Returns related a I<Koha::Hold::CancellationRequests> resultset.
609
610
=cut
611
612
sub cancellation_requests {
613
    my ($self) = @_;
614
615
    return Koha::Hold::CancellationRequests->search( { hold_id => $self->id } );
616
}
528
617
529
=head3 cancel
618
=head3 cancel
530
619
(-)a/Koha/Schema/Result/Reserve.pm (+7 lines)
Lines 491-494 __PACKAGE__->belongs_to( Link Here
491
  },
491
  },
492
);
492
);
493
493
494
__PACKAGE__->has_many(
495
  "cancellation_requests",
496
  "Koha::Schema::Result::HoldCancellationRequest",
497
  { "foreign.hold_id" => "self.reserve_id" },
498
  { cascade_copy => 0, cascade_delete => 0 },
499
);
500
494
1;
501
1;
(-)a/t/db_dependent/Koha/Hold.t (-2 / +178 lines)
Lines 19-25 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 6;
22
use Test::More tests => 8;
23
23
24
use Test::Exception;
24
use Test::Exception;
25
use Test::MockModule;
25
use Test::MockModule;
Lines 656-658 subtest 'suspend_hold() and resume() tests' => sub { Link Here
656
656
657
    $schema->storage->txn_rollback;
657
    $schema->storage->txn_rollback;
658
};
658
};
659
- 
659
660
subtest 'cancellation_requests() and add_cancellation_request() tests' => sub {
661
662
    plan tests => 4;
663
664
    $schema->storage->txn_begin;
665
666
    t::lib::Mocks::mock_preference( 'RealTimeHoldsQueue', 0 );
667
668
    my $hold = $builder->build_object( { class => 'Koha::Holds', } );
669
670
    is( $hold->cancellation_requests->count, 0 );
671
672
    # Add two cancellation requests
673
    my $request_1 = $hold->add_cancellation_request;
674
    isnt( $request_1->creation_date, undef, 'creation_date is set' );
675
676
    my $requester     = $builder->build_object( { class => 'Koha::Patrons' } );
677
    my $creation_date = '2021-06-25 14:05:35';
678
679
    my $request_2 = $hold->add_cancellation_request(
680
        {
681
            creation_date => $creation_date,
682
        }
683
    );
684
685
    is( $request_2->creation_date, $creation_date, 'Passed creation_date set' );
686
687
    is( $hold->cancellation_requests->count, 2 );
688
689
    $schema->storage->txn_rollback;
690
};
691
692
subtest 'cancellation_requestable_from_opac() tests' => sub {
693
694
    plan tests => 5;
695
696
    $schema->storage->txn_begin;
697
698
    my $category =
699
      $builder->build_object( { class => 'Koha::Patron::Categories' } );
700
    my $item_home_library =
701
      $builder->build_object( { class => 'Koha::Libraries' } );
702
    my $patron_home_library =
703
      $builder->build_object( { class => 'Koha::Libraries' } );
704
705
    my $item =
706
      $builder->build_sample_item( { library => $item_home_library->id } );
707
    my $patron = $builder->build_object(
708
        {
709
            class => 'Koha::Patrons',
710
            value => { branchcode => $patron_home_library->id }
711
        }
712
    );
713
714
    subtest 'Exception cases' => sub {
715
716
        plan tests => 4;
717
718
        my $hold = $builder->build_object(
719
            {
720
                class => 'Koha::Holds',
721
                value => {
722
                    itemnumber     => undef,
723
                    found          => undef,
724
                    borrowernumber => $patron->id
725
                }
726
            }
727
        );
728
729
        throws_ok { $hold->cancellation_requestable_from_opac; }
730
        'Koha::Exceptions::InvalidStatus',
731
          'Exception thrown because hold is not waiting';
732
733
        is( $@->invalid_status, 'hold_not_waiting' );
734
735
        $hold = $builder->build_object(
736
            {
737
                class => 'Koha::Holds',
738
                value => {
739
                    itemnumber     => undef,
740
                    found          => 'W',
741
                    borrowernumber => $patron->id
742
                }
743
            }
744
        );
745
746
        throws_ok { $hold->cancellation_requestable_from_opac; }
747
        'Koha::Exceptions::InvalidStatus',
748
          'Exception thrown because waiting hold has no item linked';
749
750
        is( $@->invalid_status, 'no_item_linked' );
751
    };
752
753
    # set default rule to enabled
754
    Koha::CirculationRules->set_rule(
755
        {
756
            categorycode => '*',
757
            itemtype     => '*',
758
            branchcode   => '*',
759
            rule_name    => 'waiting_hold_cancellation',
760
            rule_value   => 1,
761
        }
762
    );
763
764
    my $hold = $builder->build_object(
765
        {
766
            class => 'Koha::Holds',
767
            value => {
768
                itemnumber     => $item->id,
769
                found          => 'W',
770
                borrowernumber => $patron->id
771
            }
772
        }
773
    );
774
775
    t::lib::Mocks::mock_preference( 'ReservesControlBranch',
776
        'ItemHomeLibrary' );
777
778
    Koha::CirculationRules->set_rule(
779
        {
780
            categorycode => $patron->categorycode,
781
            itemtype     => $item->itype,
782
            branchcode   => $item->homebranch,
783
            rule_name    => 'waiting_hold_cancellation',
784
            rule_value   => 0,
785
        }
786
    );
787
788
    ok( !$hold->cancellation_requestable_from_opac );
789
790
    Koha::CirculationRules->set_rule(
791
        {
792
            categorycode => $patron->categorycode,
793
            itemtype     => $item->itype,
794
            branchcode   => $item->homebranch,
795
            rule_name    => 'waiting_hold_cancellation',
796
            rule_value   => 1,
797
        }
798
    );
799
800
    ok(
801
        $hold->cancellation_requestable_from_opac,
802
        'Make sure it is picking the right circulation rule'
803
    );
804
805
    t::lib::Mocks::mock_preference( 'ReservesControlBranch', 'PatronLibrary' );
806
807
    Koha::CirculationRules->set_rule(
808
        {
809
            categorycode => $patron->categorycode,
810
            itemtype     => $item->itype,
811
            branchcode   => $patron->branchcode,
812
            rule_name    => 'waiting_hold_cancellation',
813
            rule_value   => 0,
814
        }
815
    );
816
817
    ok( !$hold->cancellation_requestable_from_opac );
818
819
    Koha::CirculationRules->set_rule(
820
        {
821
            categorycode => $patron->categorycode,
822
            itemtype     => $item->itype,
823
            branchcode   => $patron->branchcode,
824
            rule_name    => 'waiting_hold_cancellation',
825
            rule_value   => 1,
826
        }
827
    );
828
829
    ok(
830
        $hold->cancellation_requestable_from_opac,
831
        'Make sure it is picking the right circulation rule'
832
    );
833
834
    $schema->storage->txn_rollback;
835
};

Return to bug 22456