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 486-489 __PACKAGE__->belongs_to( Link Here
486
  },
486
  },
487
);
487
);
488
488
489
__PACKAGE__->has_many(
490
  "cancellation_requests",
491
  "Koha::Schema::Result::HoldCancellationRequest",
492
  { "foreign.hold_id" => "self.reserve_id" },
493
  { cascade_copy => 0, cascade_delete => 0 },
494
);
495
489
1;
496
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 => 7;
22
use Test::More tests => 9;
23
23
24
use Test::Exception;
24
use Test::Exception;
25
use Test::MockModule;
25
use Test::MockModule;
Lines 686-688 subtest 'suspend_hold() and resume() tests' => sub { Link Here
686
686
687
    $schema->storage->txn_rollback;
687
    $schema->storage->txn_rollback;
688
};
688
};
689
- 
689
690
subtest 'cancellation_requests() and add_cancellation_request() tests' => sub {
691
692
    plan tests => 4;
693
694
    $schema->storage->txn_begin;
695
696
    t::lib::Mocks::mock_preference( 'RealTimeHoldsQueue', 0 );
697
698
    my $hold = $builder->build_object( { class => 'Koha::Holds', } );
699
700
    is( $hold->cancellation_requests->count, 0 );
701
702
    # Add two cancellation requests
703
    my $request_1 = $hold->add_cancellation_request;
704
    isnt( $request_1->creation_date, undef, 'creation_date is set' );
705
706
    my $requester     = $builder->build_object( { class => 'Koha::Patrons' } );
707
    my $creation_date = '2021-06-25 14:05:35';
708
709
    my $request_2 = $hold->add_cancellation_request(
710
        {
711
            creation_date => $creation_date,
712
        }
713
    );
714
715
    is( $request_2->creation_date, $creation_date, 'Passed creation_date set' );
716
717
    is( $hold->cancellation_requests->count, 2 );
718
719
    $schema->storage->txn_rollback;
720
};
721
722
subtest 'cancellation_requestable_from_opac() tests' => sub {
723
724
    plan tests => 5;
725
726
    $schema->storage->txn_begin;
727
728
    my $category =
729
      $builder->build_object( { class => 'Koha::Patron::Categories' } );
730
    my $item_home_library =
731
      $builder->build_object( { class => 'Koha::Libraries' } );
732
    my $patron_home_library =
733
      $builder->build_object( { class => 'Koha::Libraries' } );
734
735
    my $item =
736
      $builder->build_sample_item( { library => $item_home_library->id } );
737
    my $patron = $builder->build_object(
738
        {
739
            class => 'Koha::Patrons',
740
            value => { branchcode => $patron_home_library->id }
741
        }
742
    );
743
744
    subtest 'Exception cases' => sub {
745
746
        plan tests => 4;
747
748
        my $hold = $builder->build_object(
749
            {
750
                class => 'Koha::Holds',
751
                value => {
752
                    itemnumber     => undef,
753
                    found          => undef,
754
                    borrowernumber => $patron->id
755
                }
756
            }
757
        );
758
759
        throws_ok { $hold->cancellation_requestable_from_opac; }
760
        'Koha::Exceptions::InvalidStatus',
761
          'Exception thrown because hold is not waiting';
762
763
        is( $@->invalid_status, 'hold_not_waiting' );
764
765
        $hold = $builder->build_object(
766
            {
767
                class => 'Koha::Holds',
768
                value => {
769
                    itemnumber     => undef,
770
                    found          => 'W',
771
                    borrowernumber => $patron->id
772
                }
773
            }
774
        );
775
776
        throws_ok { $hold->cancellation_requestable_from_opac; }
777
        'Koha::Exceptions::InvalidStatus',
778
          'Exception thrown because waiting hold has no item linked';
779
780
        is( $@->invalid_status, 'no_item_linked' );
781
    };
782
783
    # set default rule to enabled
784
    Koha::CirculationRules->set_rule(
785
        {
786
            categorycode => '*',
787
            itemtype     => '*',
788
            branchcode   => '*',
789
            rule_name    => 'waiting_hold_cancellation',
790
            rule_value   => 1,
791
        }
792
    );
793
794
    my $hold = $builder->build_object(
795
        {
796
            class => 'Koha::Holds',
797
            value => {
798
                itemnumber     => $item->id,
799
                found          => 'W',
800
                borrowernumber => $patron->id
801
            }
802
        }
803
    );
804
805
    t::lib::Mocks::mock_preference( 'ReservesControlBranch',
806
        'ItemHomeLibrary' );
807
808
    Koha::CirculationRules->set_rule(
809
        {
810
            categorycode => $patron->categorycode,
811
            itemtype     => $item->itype,
812
            branchcode   => $item->homebranch,
813
            rule_name    => 'waiting_hold_cancellation',
814
            rule_value   => 0,
815
        }
816
    );
817
818
    ok( !$hold->cancellation_requestable_from_opac );
819
820
    Koha::CirculationRules->set_rule(
821
        {
822
            categorycode => $patron->categorycode,
823
            itemtype     => $item->itype,
824
            branchcode   => $item->homebranch,
825
            rule_name    => 'waiting_hold_cancellation',
826
            rule_value   => 1,
827
        }
828
    );
829
830
    ok(
831
        $hold->cancellation_requestable_from_opac,
832
        'Make sure it is picking the right circulation rule'
833
    );
834
835
    t::lib::Mocks::mock_preference( 'ReservesControlBranch', 'PatronLibrary' );
836
837
    Koha::CirculationRules->set_rule(
838
        {
839
            categorycode => $patron->categorycode,
840
            itemtype     => $item->itype,
841
            branchcode   => $patron->branchcode,
842
            rule_name    => 'waiting_hold_cancellation',
843
            rule_value   => 0,
844
        }
845
    );
846
847
    ok( !$hold->cancellation_requestable_from_opac );
848
849
    Koha::CirculationRules->set_rule(
850
        {
851
            categorycode => $patron->categorycode,
852
            itemtype     => $item->itype,
853
            branchcode   => $patron->branchcode,
854
            rule_name    => 'waiting_hold_cancellation',
855
            rule_value   => 1,
856
        }
857
    );
858
859
    ok(
860
        $hold->cancellation_requestable_from_opac,
861
        'Make sure it is picking the right circulation rule'
862
    );
863
864
    $schema->storage->txn_rollback;
865
};

Return to bug 22456