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

(-)a/Koha/Exceptions/Hold.pm (+4 lines)
Lines 31-36 use Exception::Class ( Link Here
31
    'Koha::Exceptions::Hold::InvalidPickupLocation' => {
31
    'Koha::Exceptions::Hold::InvalidPickupLocation' => {
32
        isa         => 'Koha::Exceptions::Hold',
32
        isa         => 'Koha::Exceptions::Hold',
33
        description => 'The supplied pickup location is not valid'
33
        description => 'The supplied pickup location is not valid'
34
    },
35
    'Koha::Exceptions::Hold::UnexpectedStatus' => {
36
        isa         => 'Koha::Exceptions::Hold',
37
        description => 'The context expects another status',
34
    }
38
    }
35
);
39
);
36
40
(-)a/Koha/Hold.pm (+101 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 416-421 sub is_cancelable_from_opac { Link Here
416
    return 0; # if ->is_in_transit or if ->is_waiting or ->is_in_processing
417
    return 0; # if ->is_in_transit or if ->is_waiting or ->is_in_processing
417
}
418
}
418
419
420
=head3 cancellation_requestable_from_opac
421
422
    if ( $hold->cancellation_requestable_from_opac ) { ... }
423
424
Returns a I<boolean> representing if a cancellation request can be placed on the hold
425
from the OPAC. It targets holds that cannot be cancelled from the OPAC (see the
426
B<is_cancelable_from_opac> method above), but for which circulation rules allow
427
requesting cancellation.
428
429
=cut
430
431
sub cancellation_requestable_from_opac {
432
    my ( $self ) = @_;
433
434
    Koha::Exceptions::Hold::UnexpectedStatus->throw("Cancellation can only be asked for waiting holds")
435
      unless $self->is_waiting;
436
437
    my $item = $self->item;
438
439
    Koha::Exceptions::Hold::UnexpectedStatus->throw("Waiting holds are expected to have an item linked")
440
      unless $item;
441
442
    my $patron = $self->patron;
443
444
    my $controlbranch = $patron->branchcode;
445
446
    if ( C4::Context->preference('ReservesControlBranch') eq 'ItemHomeLibrary' ) {
447
        $controlbranch = $item->homebranch;
448
    }
449
450
    return Koha::CirculationRules->get_effective_rule(
451
        {
452
            categorycode => $patron->categorycode,
453
            itemtype     => $item->itype,
454
            branchcode   => $controlbranch,
455
            rule_name    => 'waiting_hold_cancellation',
456
        }
457
    )->rule_value ? 1 : 0;
458
}
459
460
=head3 cancellation_requested_by_owner
461
462
    if ( $hold->cancellation_requested_by_owner ) { ... }
463
464
Returns a I<boolean> representing if a cancellation request has been placed by
465
to hold owner.
466
467
=cut
468
469
sub cancellation_requested_by_owner {
470
    my ($self) = @_;
471
472
    return $self->cancellation_requests->search(
473
        { requester_id => $self->borrowernumber } )->count > 0;
474
}
475
419
=head3 is_at_destination
476
=head3 is_at_destination
420
477
421
Returns true if hold is waiting
478
Returns true if hold is waiting
Lines 525-530 sub is_suspended { Link Here
525
    return $self->suspend();
582
    return $self->suspend();
526
}
583
}
527
584
585
=head3 add_cancellation_request
586
587
    my $cancellation_request = $hold->add_cancellation_request(
588
        {
589
          [ requester_id  => $patron->id,
590
            creation_date => $creation_date,
591
            source        => 'owner', ]
592
        }
593
    );
594
595
Adds a cancellation request to the hold. Returns the generated
596
I<Koha::Hold::CancellationRequest> object.
597
598
=cut
599
600
sub add_cancellation_request {
601
    my ( $self, $params ) = @_;
602
603
    my $request = Koha::Hold::CancellationRequest->new(
604
        {   hold_id      => $self->id,
605
            requester_id => $params->{requester_id},
606
            ( $params->{creation_date} ? ( creation_date => $params->{creation_date} ) : () ),
607
            source => $params->{source},
608
        }
609
    )->store;
610
611
    $request->discard_changes;
612
613
    return $request;
614
}
615
616
=head3 cancellation_requests
617
618
    my $cancellation_requests = $hold->cancellation_requests;
619
620
Returns related a I<Koha::Hold::CancellationRequests> resultset.
621
622
=cut
623
624
sub cancellation_requests {
625
    my ($self) = @_;
626
627
    return Koha::Hold::CancellationRequests->search( { hold_id => $self->id } );
628
}
528
629
529
=head3 cancel
630
=head3 cancel
530
631
(-)a/t/db_dependent/Koha/Hold.t (-2 / +194 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 => 8;
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
675
    is( $request_1->requester_id, undef );
676
    is( $request_1->status, 'requested', 'Default value set' );
677
    isnt( $request_1->creation_date, undef );
678
679
    my $requester     = $builder->build_object( { class => 'Koha::Patrons' } );
680
    my $creation_date = '2021-06-25 14:05:35';
681
682
    my $request_2 = $hold->add_cancellation_request(
683
        {
684
            requester_id  => $requester->id,
685
            creation_date => $creation_date,
686
        }
687
    );
688
689
    is( $request_2->requester_id, $requester->id, 'Passed requester_id set' );
690
    is( $request_2->creation_date, $creation_date, 'Passed creation_date set' );
691
692
    is( $hold->cancellation_requests->count, 2 );
693
694
    subtest 'cancellation_requested_by_owner() tests' => sub {
695
696
        plan tests => 2;
697
698
        ok( !$hold->cancellation_requested_by_owner );
699
700
        $hold->add_cancellation_request({ requester_id => $hold->borrowernumber });
701
702
        ok( $hold->cancellation_requested_by_owner );
703
    };
704
705
    $schema->storage->txn_rollback;
706
};
707
708
subtest 'cancellation_requestable_from_opac() tests' => sub {
709
710
    plan tests => 5;
711
712
    $schema->storage->txn_begin;
713
714
    my $category =
715
      $builder->build_object( { class => 'Koha::Patron::Categories' } );
716
    my $item_home_library =
717
      $builder->build_object( { class => 'Koha::Libraries' } );
718
    my $patron_home_library =
719
      $builder->build_object( { class => 'Koha::Libraries' } );
720
721
    my $item =
722
      $builder->build_sample_item( { library => $item_home_library->id } );
723
    my $patron = $builder->build_object(
724
        {
725
            class => 'Koha::Patrons',
726
            value => { branchcode => $patron_home_library->id }
727
        }
728
    );
729
730
    subtest 'Exception cases' => sub {
731
732
        plan tests => 4;
733
734
        my $hold = $builder->build_object(
735
            {
736
                class => 'Koha::Holds',
737
                value => {
738
                    itemnumber     => undef,
739
                    found          => undef,
740
                    borrowernumber => $patron->id
741
                }
742
            }
743
        );
744
745
        throws_ok { $hold->cancellation_requestable_from_opac; }
746
        'Koha::Exceptions::Hold::UnexpectedStatus',
747
          'Exception thrown because hold is not waiting';
748
749
        is( "$@", "Cancellation can only be asked for waiting holds" );
750
751
        $hold = $builder->build_object(
752
            {
753
                class => 'Koha::Holds',
754
                value => {
755
                    itemnumber     => undef,
756
                    found          => 'W',
757
                    borrowernumber => $patron->id
758
                }
759
            }
760
        );
761
762
        throws_ok { $hold->cancellation_requestable_from_opac; }
763
        'Koha::Exceptions::Hold::UnexpectedStatus',
764
          'Exception thrown because waiting hold has no item linked';
765
766
        is( "$@", "Waiting holds are expected to have an item linked" );
767
    };
768
769
    # set default rule to enabled
770
    Koha::CirculationRules->set_rule(
771
        {
772
            categorycode => '*',
773
            itemtype     => '*',
774
            branchcode   => '*',
775
            rule_name    => 'waiting_hold_cancellation',
776
            rule_value   => 1,
777
        }
778
    );
779
780
    my $hold = $builder->build_object(
781
        {
782
            class => 'Koha::Holds',
783
            value => {
784
                itemnumber     => $item->id,
785
                found          => 'W',
786
                borrowernumber => $patron->id
787
            }
788
        }
789
    );
790
791
    t::lib::Mocks::mock_preference( 'ReservesControlBranch',
792
        'ItemHomeLibrary' );
793
794
    Koha::CirculationRules->set_rule(
795
        {
796
            categorycode => $patron->categorycode,
797
            itemtype     => $item->itype,
798
            branchcode   => $item->homebranch,
799
            rule_name    => 'waiting_hold_cancellation',
800
            rule_value   => 0,
801
        }
802
    );
803
804
    ok( !$hold->cancellation_requestable_from_opac );
805
806
    Koha::CirculationRules->set_rule(
807
        {
808
            categorycode => $patron->categorycode,
809
            itemtype     => $item->itype,
810
            branchcode   => $item->homebranch,
811
            rule_name    => 'waiting_hold_cancellation',
812
            rule_value   => 1,
813
        }
814
    );
815
816
    ok(
817
        $hold->cancellation_requestable_from_opac,
818
        'Make sure it is picking the right circulation rule'
819
    );
820
821
    t::lib::Mocks::mock_preference( 'ReservesControlBranch', 'PatronLibrary' );
822
823
    Koha::CirculationRules->set_rule(
824
        {
825
            categorycode => $patron->categorycode,
826
            itemtype     => $item->itype,
827
            branchcode   => $patron->branchcode,
828
            rule_name    => 'waiting_hold_cancellation',
829
            rule_value   => 0,
830
        }
831
    );
832
833
    ok( !$hold->cancellation_requestable_from_opac );
834
835
    Koha::CirculationRules->set_rule(
836
        {
837
            categorycode => $patron->categorycode,
838
            itemtype     => $item->itype,
839
            branchcode   => $patron->branchcode,
840
            rule_name    => 'waiting_hold_cancellation',
841
            rule_value   => 1,
842
        }
843
    );
844
845
    ok(
846
        $hold->cancellation_requestable_from_opac,
847
        'Make sure it is picking the right circulation rule'
848
    );
849
850
    $schema->storage->txn_rollback;
851
};

Return to bug 22456