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

(-)a/C4/Circulation.pm (-136 / +42 lines)
Lines 1541-1591 sub AddIssue { Link Here
1541
            $item_object->datelastseen( dt_from_string()->ymd() );
1541
            $item_object->datelastseen( dt_from_string()->ymd() );
1542
            $item_object->store({log_action => 0});
1542
            $item_object->store({log_action => 0});
1543
1543
1544
            # If the item was lost, it has now been found, restore/charge the overdue if necessary
1544
            # If the item was lost, it has now been found, charge the overdue if necessary
1545
            if ($was_lost) {
1545
            if ($was_lost) {
1546
                my $lostreturn_policy =
1546
                if ( $item_object->{_charge} ) {
1547
                  Koha::CirculationRules->get_lostreturn_policy(
1547
                    $actualissue //= Koha::Old::Checkouts->search(
1548
                    {
1548
                        { itemnumber => $item_unblessed->{itemnumber} },
1549
                        return_branch => C4::Context->userenv->{branch},
1549
                        {
1550
                        item          => $item_object
1550
                            order_by => { '-desc' => 'returndate' },
1551
                    }
1551
                            rows     => 1
1552
                  );
1553
1554
                if ($lostreturn_policy) {
1555
1556
                    if ( $lostreturn_policy eq 'charge' ) {
1557
                        $actualissue //= Koha::Old::Checkouts->search(
1558
                            { itemnumber => $item_unblessed->{itemnumber} },
1559
                            {
1560
                                order_by => { '-desc' => 'returndate' },
1561
                                rows     => 1
1562
                            }
1563
                        )->single;
1564
                        unless ( exists( $borrower->{branchcode} ) ) {
1565
                            my $patron = $actualissue->patron;
1566
                            $borrower = $patron->unblessed;
1567
                        }
1552
                        }
1568
                        _CalculateAndUpdateFine(
1553
                    )->single;
1569
                            {
1554
                    unless ( exists( $borrower->{branchcode} ) ) {
1570
                                issue       => $actualissue,
1555
                        my $patron = $actualissue->patron;
1571
                                item        => $item_unblessed,
1556
                        $borrower = $patron->unblessed;
1572
                                borrower    => $borrower,
1573
                                return_date => $issuedate
1574
                            }
1575
                        );
1576
                        _FixOverduesOnReturn( $borrower->{borrowernumber},
1577
                            $item_object->itemnumber, undef, 'RENEWED' );
1578
                    }
1579
                    elsif ( $lostreturn_policy eq 'restore' ) {
1580
                        _RestoreOverdueForLostAndFound(
1581
                            $item_object->itemnumber );
1582
                    }
1583
1584
                    if ( C4::Context->preference('AccountAutoReconcile') ) {
1585
                        $account->reconcile_balance;
1586
                    }
1557
                    }
1558
                    _CalculateAndUpdateFine(
1559
                        {
1560
                            issue       => $actualissue,
1561
                            item        => $item_unblessed,
1562
                            borrower    => $borrower,
1563
                            return_date => $issuedate
1564
                        }
1565
                    );
1566
                    _FixOverduesOnReturn( $borrower->{borrowernumber},
1567
                        $item_object->itemnumber, undef, 'RENEWED' );
1587
                }
1568
                }
1588
1589
            }
1569
            }
1590
1570
1591
            # If it costs to borrow this book, charge it to the patron's account.
1571
            # If it costs to borrow this book, charge it to the patron's account.
Lines 2094-2139 sub AddReturn { Link Here
2094
    my $updated_item = ModDateLastSeen( $item->itemnumber, $leave_item_lost, { skip_record_index => 1 } ); # will unset itemlost if needed
2074
    my $updated_item = ModDateLastSeen( $item->itemnumber, $leave_item_lost, { skip_record_index => 1 } ); # will unset itemlost if needed
2095
2075
2096
    # fix up the accounts.....
2076
    # fix up the accounts.....
2097
    if ( $item_was_lost ) {
2077
    if ($item_was_lost) {
2098
        $messages->{'WasLost'} = 1;
2078
        $messages->{'WasLost'} = 1;
2099
        unless ( C4::Context->preference("BlockReturnOfLostItems") ) {
2079
        unless ( C4::Context->preference("BlockReturnOfLostItems") ) {
2100
            $messages->{'LostItemFeeRefunded'} = $updated_item->{_refunded};
2080
            $messages->{'LostItemFeeRefunded'} = $updated_item->{_refunded};
2101
2081
            $messages->{'LostItemFeeRestored'} = $updated_item->{_restored};
2102
            my $lostreturn_policy =
2082
2103
              Koha::CirculationRules->get_lostreturn_policy(
2083
            if ( $updated_item->{_charge} ) {
2104
                {
2084
                $issue //= Koha::Old::Checkouts->search(
2105
                    return_branch => C4::Context->userenv->{branch},
2085
                    { itemnumber => $item->itemnumber },
2106
                    item          => $updated_item
2086
                    { order_by   => { '-desc' => 'returndate' }, rows => 1 } )
2087
                  ->single;
2088
                unless ( exists( $patron_unblessed->{branchcode} ) ) {
2089
                    my $patron = $issue->patron;
2090
                    $patron_unblessed = $patron->unblessed;
2107
                }
2091
                }
2108
              );
2092
                _CalculateAndUpdateFine(
2109
2093
                    {
2110
            if ($lostreturn_policy) {
2094
                        issue       => $issue,
2111
2095
                        item        => $item->unblessed,
2112
                if ( $lostreturn_policy eq 'charge' ) {
2096
                        borrower    => $patron_unblessed,
2113
                    $issue //= Koha::Old::Checkouts->search(
2097
                        return_date => $return_date
2114
                        { itemnumber => $item->itemnumber },
2115
                        { order_by   => { '-desc' => 'returndate' }, rows => 1 }
2116
                    )->single;
2117
                    unless (exists($patron_unblessed->{branchcode})) {
2118
                        my $patron = $issue->patron;
2119
                        $patron_unblessed = $patron->unblessed;
2120
                    }
2098
                    }
2121
                    _CalculateAndUpdateFine(
2099
                );
2122
                        {
2100
                _FixOverduesOnReturn( $patron_unblessed->{borrowernumber},
2123
                            issue       => $issue,
2101
                    $item->itemnumber, undef, 'RETURNED' );
2124
                            item        => $item->unblessed,
2102
                $messages->{'LostItemFeeCharged'} = 1;
2125
                            borrower    => $patron_unblessed,
2126
                            return_date => $return_date
2127
                        }
2128
                    );
2129
                    _FixOverduesOnReturn( $patron_unblessed->{borrowernumber},
2130
                        $item->itemnumber, undef, 'RETURNED' );
2131
                    $messages->{'LostItemFeeCharged'} = 1;
2132
                }
2133
                elsif ( $lostreturn_policy eq 'restore' ) {
2134
                    _RestoreOverdueForLostAndFound( $item->itemnumber );
2135
                    $messages->{'LostItemFeeRestored'} = 1;
2136
                }
2137
            }
2103
            }
2138
        }
2104
        }
2139
    }
2105
    }
Lines 2593-2658 sub _FixOverduesOnReturn { Link Here
2593
    return $result;
2559
    return $result;
2594
}
2560
}
2595
2561
2596
=head2 _RestoreOverdueForLostAndFound
2597
2598
   &_RestoreOverdueForLostAndFound( $itemnumber );
2599
2600
C<$itemnumber> itemnumber
2601
2602
Internal function
2603
2604
=cut
2605
2606
sub _RestoreOverdueForLostAndFound {
2607
    my ( $item ) = @_;
2608
2609
    unless( $item ) {
2610
        warn "_RestoreOverdueForLostAndFound() not supplied valid itemnumber";
2611
        return;
2612
    }
2613
2614
    my $schema = Koha::Database->schema;
2615
2616
    my $result = $schema->txn_do(
2617
        sub {
2618
            # check for lost overdue fine
2619
            my $accountlines = Koha::Account::Lines->search(
2620
                {
2621
                    itemnumber      => $item,
2622
                    debit_type_code => 'OVERDUE',
2623
                    status          => 'LOST'
2624
                },
2625
                {
2626
                    order_by => { '-desc' => 'date' },
2627
                    rows     => 1
2628
                }
2629
            );
2630
            return 0 unless $accountlines->count; # no warning, there's just nothing to fix
2631
2632
            # Update status of fine
2633
            my $overdue = $accountlines->next;
2634
            $overdue->status('RETURNED')->store();
2635
2636
            # Find related forgive credit
2637
            my $refunds = $overdue->credits(
2638
                {
2639
                    credit_type_code => 'FORGIVEN',
2640
                    itemnumber       => $item,
2641
                    status           => [ { '!=' => 'VOID' }, undef ]
2642
                },
2643
                { order_by => { '-desc' => 'date' }, rows => 1 }
2644
            );
2645
            return 0 unless $refunds->count; # no warning, there's just nothing to fix
2646
2647
            # Revert the forgive credit
2648
            my $refund = $refunds->next;
2649
            return $refund->void();
2650
        }
2651
    );
2652
2653
    return $result;
2654
}
2655
2656
=head2 _GetCircControlBranch
2562
=head2 _GetCircControlBranch
2657
2563
2658
   my $circ_control_branch = _GetCircControlBranch($iteminfos, $borrower);
2564
   my $circ_control_branch = _GetCircControlBranch($iteminfos, $borrower);
(-)a/Koha/Item.pm (-71 / +120 lines)
Lines 825-832 sub _set_found_trigger { Link Here
825
        return $self unless $lost_age_in_days < $no_refund_after_days;
825
        return $self unless $lost_age_in_days < $no_refund_after_days;
826
    }
826
    }
827
827
828
    return $self
828
    my $lostreturn_policy = Koha::CirculationRules->get_lostreturn_policy(
829
      unless Koha::CirculationRules->get_lostreturn_policy(
830
        {
829
        {
831
            item          => $self,
830
            item          => $self,
832
            return_branch => C4::Context->userenv
831
            return_branch => C4::Context->userenv
Lines 835-914 sub _set_found_trigger { Link Here
835
        }
834
        }
836
      );
835
      );
837
836
838
    # check for charge made for lost book
837
    if ( $lostreturn_policy ) {
839
    my $accountlines = Koha::Account::Lines->search(
840
        {
841
            itemnumber      => $self->itemnumber,
842
            debit_type_code => 'LOST',
843
            status          => [ undef, { '<>' => 'FOUND' } ]
844
        },
845
        {
846
            order_by => { -desc => [ 'date', 'accountlines_id' ] }
847
        }
848
    );
849
850
    return $self unless $accountlines->count > 0;
851
852
    my $accountline     = $accountlines->next;
853
    my $total_to_refund = 0;
854
838
855
    return $self unless $accountline->borrowernumber;
839
        # refund charge made for lost book
856
840
        my $lost_charge = Koha::Account::Lines->search(
857
    my $patron = Koha::Patrons->find( $accountline->borrowernumber );
858
    return $self
859
      unless $patron;  # Patron has been deleted, nobody to credit the return to
860
                       # FIXME Should not we notify this somewhere
861
862
    my $account = $patron->account;
863
864
    # Use cases
865
    if ( $accountline->amount > $accountline->amountoutstanding ) {
866
867
    # some amount has been cancelled. collect the offsets that are not writeoffs
868
    # this works because the only way to subtract from this kind of a debt is
869
    # using the UI buttons 'Pay' and 'Write off'
870
        my $credits_offsets = Koha::Account::Offsets->search(
871
            {
841
            {
872
                debit_id  => $accountline->id,
842
                itemnumber      => $self->itemnumber,
873
                credit_id => { '!=' => undef },     # it is not the debit itself
843
                debit_type_code => 'LOST',
874
                type      => { '!=' => 'Writeoff' },
844
                status          => [ undef, { '<>' => 'FOUND' } ]
875
                amount => { '<' => 0 }    # credits are negative on the DB
845
            },
876
            }
877
        );
878
879
        $total_to_refund = ( $credits_offsets->count > 0 )
880
          ? $credits_offsets->total * -1    # credits are negative on the DB
881
          : 0;
882
    }
883
884
    my $credit_total = $accountline->amountoutstanding + $total_to_refund;
885
886
    my $credit;
887
    if ( $credit_total > 0 ) {
888
        my $branchcode =
889
          C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef;
890
        $credit = $account->add_credit(
891
            {
846
            {
892
                amount      => $credit_total,
847
                order_by => { -desc => [ 'date', 'accountlines_id' ] },
893
                description => 'Item found ' . $self->itemnumber,
848
                rows     => 1
894
                type        => 'LOST_FOUND',
895
                interface   => C4::Context->interface,
896
                library_id  => $branchcode,
897
                item_id     => $self->itemnumber,
898
                issue_id    => $accountline->issue_id
899
            }
849
            }
900
        );
850
        )->single;
901
851
902
        $credit->apply( { debits => [$accountline] } );
852
        if ( $lost_charge ) {
903
        $self->{_refunded} = 1;
853
904
    }
854
            my $patron = $lost_charge->patron;
905
855
            if ( $patron ) {
906
    # Update the account status
856
907
    $accountline->status('FOUND');
857
                my $account = $patron->account;
908
    $accountline->store();
858
                my $total_to_refund = 0;
859
860
                # Use cases
861
                if ( $lost_charge->amount > $lost_charge->amountoutstanding ) {
862
863
                    # some amount has been cancelled. collect the offsets that are not writeoffs
864
                    # this works because the only way to subtract from this kind of a debt is
865
                    # using the UI buttons 'Pay' and 'Write off'
866
                    my $credits_offsets = Koha::Account::Offsets->search(
867
                        {
868
                            debit_id  => $lost_charge->id,
869
                            credit_id => { '!=' => undef },     # it is not the debit itself
870
                            type      => { '!=' => 'Writeoff' },
871
                            amount    => { '<' => 0 }    # credits are negative on the DB
872
                        }
873
                    );
874
875
                    $total_to_refund = ( $credits_offsets->count > 0 )
876
                      ? $credits_offsets->total * -1    # credits are negative on the DB
877
                      : 0;
878
                }
879
880
                my $credit_total = $lost_charge->amountoutstanding + $total_to_refund;
881
882
                my $credit;
883
                if ( $credit_total > 0 ) {
884
                    my $branchcode =
885
                      C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef;
886
                    $credit = $account->add_credit(
887
                        {
888
                            amount      => $credit_total,
889
                            description => 'Item found ' . $self->itemnumber,
890
                            type        => 'LOST_FOUND',
891
                            interface   => C4::Context->interface,
892
                            library_id  => $branchcode,
893
                            item_id     => $self->itemnumber,
894
                            issue_id    => $lost_charge->issue_id
895
                        }
896
                    );
897
898
                    $credit->apply( { debits => [$lost_charge] } );
899
                    $self->{_refunded} = 1;
900
                }
901
902
                # Update the account status
903
                $lost_charge->status('FOUND');
904
                $lost_charge->store();
905
906
                # Reconcile balances if required
907
                if ( C4::Context->preference('AccountAutoReconcile') ) {
908
                    $account->reconcile_balance;
909
                }
910
            }
911
        }
909
912
910
    if ( defined $account and C4::Context->preference('AccountAutoReconcile') ) {
913
        # restore fine for lost book
911
        $account->reconcile_balance;
914
        if ( $lostreturn_policy eq 'restore' ) {
915
            my $lost_overdue = Koha::Account::Lines->search(
916
                {
917
                    itemnumber      => $self->itemnumber,
918
                    debit_type_code => 'OVERDUE',
919
                    status          => 'LOST'
920
                },
921
                {
922
                    order_by => { '-desc' => 'date' },
923
                    rows     => 1
924
                }
925
            )->single;
926
927
            if ( $lost_overdue ) {
928
929
                my $patron = $lost_overdue->patron;
930
                if ($patron) {
931
                    my $account = $patron->account;
932
933
                    # Update status of fine
934
                    $lost_overdue->status('FOUND')->store();
935
936
                    # Find related forgive credit
937
                    my $refund = $lost_overdue->credits(
938
                        {
939
                            credit_type_code => 'FORGIVEN',
940
                            itemnumber       => $self->itemnumber,
941
                            status           => [ { '!=' => 'VOID' }, undef ]
942
                        },
943
                        { order_by => { '-desc' => 'date' }, rows => 1 }
944
                    )->single;
945
946
                    if ( $refund ) {
947
                        # Revert the forgive credit
948
                        $refund->void();
949
                        $self->{_restored} = 1;
950
                    }
951
952
                    # Reconcile balances if required
953
                    if ( C4::Context->preference('AccountAutoReconcile') ) {
954
                        $account->reconcile_balance;
955
                    }
956
                }
957
            }
958
        } elsif ( $lostreturn_policy eq 'charge' ) {
959
            $self->{_charge} = 1;
960
        }
912
    }
961
    }
913
962
914
    return $self;
963
    return $self;
(-)a/t/db_dependent/Circulation.t (-65 lines)
Lines 2698-2768 subtest 'AddReturn | is_overdue' => sub { Link Here
2698
    };
2698
    };
2699
};
2699
};
2700
2700
2701
subtest '_RestoreOverdueForLostAndFound' => sub {
2702
2703
    plan tests => 7;
2704
2705
    my $manager = $builder->build_object( { class => "Koha::Patrons" } );
2706
    t::lib::Mocks::mock_userenv(
2707
        { patron => $manager, branchcode => $manager->branchcode } );
2708
2709
    my $patron = $builder->build_object( { class => "Koha::Patrons" } );
2710
    my $item = $builder->build_sample_item();
2711
2712
    # No fine found
2713
    my $result = C4::Circulation::_RestoreOverdueForLostAndFound( $item->itemnumber);
2714
    is($result, 0, "0 returned when no overdue found");
2715
2716
    # Fine not forgiven
2717
    my $account = $patron->account;
2718
    my $overdue = $account->add_debit(
2719
        {
2720
            amount     => 30.00,
2721
            user_id    => $manager->borrowernumber,
2722
            library_id => $library2->{branchcode},
2723
            interface  => 'test',
2724
            item_id    => $item->itemnumber,
2725
            type       => 'OVERDUE',
2726
        }
2727
    )->store();
2728
    $overdue->status('LOST')->store();
2729
2730
    $result = C4::Circulation::_RestoreOverdueForLostAndFound( $item->itemnumber);
2731
    is($result, 0, "0 returned when overdue found without forgival");
2732
    $overdue->discard_changes;
2733
    is($overdue->status, 'RETURNED', 'Overdue status updated to RETURNED');
2734
2735
    # Reset status
2736
    $overdue->status('LOST')->store();
2737
2738
    # Fine forgiven
2739
    my $credit = $account->add_credit(
2740
        {
2741
            amount     => 30.00,
2742
            user_id    => $manager->borrowernumber,
2743
            library_id => $library2->{branchcode},
2744
            interface  => 'test',
2745
            type       => 'FORGIVEN',
2746
            item_id    => $item->itemnumber
2747
        }
2748
    );
2749
    $credit->apply( { debits => [$overdue], offset_type => 'Forgiven' } );
2750
2751
    $result = C4::Circulation::_RestoreOverdueForLostAndFound( $item->itemnumber);
2752
2753
    is( ref($result), 'Koha::Account::Line', 'Return a Koha::Account::Line object on sucess');
2754
    $overdue->discard_changes;
2755
    is($overdue->status, 'RETURNED', 'Overdue status updated to RETURNED');
2756
    is($overdue->amountoutstanding, $overdue->amount, 'Overdue outstanding restored');
2757
2758
    # Missing parameters
2759
    warning_like {
2760
        C4::Circulation::_RestoreOverdueForLostAndFound( undef )
2761
    }
2762
    qr/_RestoreOverdueForLostAndFound\(\) not supplied valid itemnumber/,
2763
      "parameter warning received for missing itemnumbernumber";
2764
};
2765
2766
subtest '_FixOverduesOnReturn' => sub {
2701
subtest '_FixOverduesOnReturn' => sub {
2767
    plan tests => 14;
2702
    plan tests => 14;
2768
2703
(-)a/t/db_dependent/Koha/Items.t (-2 / +331 lines)
Lines 138-144 subtest 'store' => sub { Link Here
138
    };
138
    };
139
139
140
    subtest '_lost_found_trigger' => sub {
140
    subtest '_lost_found_trigger' => sub {
141
        plan tests => 7;
141
        plan tests => 10;
142
142
143
        t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
143
        t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
144
        t::lib::Mocks::mock_preference( 'WhenLostForgiveFine',          0 );
144
        t::lib::Mocks::mock_preference( 'WhenLostForgiveFine',          0 );
Lines 761-766 subtest 'store' => sub { Link Here
761
761
762
        };
762
        };
763
763
764
        subtest 'restore fine | no overdue' => sub {
765
766
            plan tests => 8;
767
768
            my $manager =
769
              $builder->build_object( { class => "Koha::Patrons" } );
770
            t::lib::Mocks::mock_userenv(
771
                { patron => $manager, branchcode => $manager->branchcode } );
772
773
            # Set lostreturn_policy to 'restore' for tests
774
            my $specific_rule_restore = $builder->build(
775
                {
776
                    source => 'CirculationRule',
777
                    value  => {
778
                        branchcode   => $manager->branchcode,
779
                        categorycode => undef,
780
                        itemtype     => undef,
781
                        rule_name    => 'lostreturn',
782
                        rule_value   => 'restore'
783
                    }
784
                }
785
            );
786
787
            my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
788
789
            my $item = $builder->build_sample_item(
790
                {
791
                    biblionumber     => $biblio->biblionumber,
792
                    library          => $library->branchcode,
793
                    replacementprice => $replacement_amount,
794
                    itype            => $item_type->itemtype
795
                }
796
            );
797
798
            my $issue =
799
              C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
800
801
            # Simulate item marked as lost
802
            $item->itemlost(1)->store;
803
            C4::Circulation::LostItem( $item->itemnumber, 1 );
804
805
            my $processing_fee_lines = Koha::Account::Lines->search(
806
                {
807
                    borrowernumber  => $patron->id,
808
                    itemnumber      => $item->itemnumber,
809
                    debit_type_code => 'PROCESSING'
810
                }
811
            );
812
            is( $processing_fee_lines->count,
813
                1, 'Only one processing fee produced' );
814
            my $processing_fee_line = $processing_fee_lines->next;
815
            is( $processing_fee_line->amount + 0,
816
                $processfee_amount,
817
                'The right PROCESSING amount is generated' );
818
            is( $processing_fee_line->amountoutstanding + 0,
819
                $processfee_amount,
820
                'The right PROCESSING amountoutstanding is generated' );
821
822
            my $lost_fee_lines = Koha::Account::Lines->search(
823
                {
824
                    borrowernumber  => $patron->id,
825
                    itemnumber      => $item->itemnumber,
826
                    debit_type_code => 'LOST'
827
                }
828
            );
829
            is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
830
            my $lost_fee_line = $lost_fee_lines->next;
831
            is( $lost_fee_line->amount + 0,
832
                $replacement_amount, 'The right LOST amount is generated' );
833
            is( $lost_fee_line->amountoutstanding + 0,
834
                $replacement_amount,
835
                'The right LOST amountountstanding is generated' );
836
837
            my $account = $patron->account;
838
            my $debts   = $account->outstanding_debits;
839
840
            # Pay off the debt
841
            my $credit = $account->add_credit(
842
                {
843
                    amount    => $account->balance,
844
                    type      => 'PAYMENT',
845
                    interface => 'test',
846
                }
847
            );
848
            $credit->apply(
849
                { debits => [ $debts->as_list ], offset_type => 'Payment' } );
850
851
            # Simulate item marked as found
852
            $item->itemlost(0)->store;
853
            is( $item->{_refunded}, 1, 'Refund triggered' );
854
            is( $item->{_restored}, undef, 'Restore not triggered when there is no overdue fine found' );
855
        };
856
857
        subtest 'restore fine | unforgiven overdue' => sub {
858
859
            plan tests => 10;
860
861
            # Set lostreturn_policy to 'restore' for tests
862
            my $manager =
863
              $builder->build_object( { class => "Koha::Patrons" } );
864
            t::lib::Mocks::mock_userenv(
865
                { patron => $manager, branchcode => $manager->branchcode } );
866
            my $specific_rule_restore = $builder->build(
867
                {
868
                    source => 'CirculationRule',
869
                    value  => {
870
                        branchcode   => $manager->branchcode,
871
                        categorycode => undef,
872
                        itemtype     => undef,
873
                        rule_name    => 'lostreturn',
874
                        rule_value   => 'restore'
875
                    }
876
                }
877
            );
878
879
            my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
880
881
            my $item = $builder->build_sample_item(
882
                {
883
                    biblionumber     => $biblio->biblionumber,
884
                    library          => $library->branchcode,
885
                    replacementprice => $replacement_amount,
886
                    itype            => $item_type->itemtype
887
                }
888
            );
889
890
            my $issue =
891
              C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
892
893
            # Simulate item marked as lost
894
            $item->itemlost(1)->store;
895
            C4::Circulation::LostItem( $item->itemnumber, 1 );
896
897
            my $processing_fee_lines = Koha::Account::Lines->search(
898
                {
899
                    borrowernumber  => $patron->id,
900
                    itemnumber      => $item->itemnumber,
901
                    debit_type_code => 'PROCESSING'
902
                }
903
            );
904
            is( $processing_fee_lines->count,
905
                1, 'Only one processing fee produced' );
906
            my $processing_fee_line = $processing_fee_lines->next;
907
            is( $processing_fee_line->amount + 0,
908
                $processfee_amount,
909
                'The right PROCESSING amount is generated' );
910
            is( $processing_fee_line->amountoutstanding + 0,
911
                $processfee_amount,
912
                'The right PROCESSING amountoutstanding is generated' );
913
914
            my $lost_fee_lines = Koha::Account::Lines->search(
915
                {
916
                    borrowernumber  => $patron->id,
917
                    itemnumber      => $item->itemnumber,
918
                    debit_type_code => 'LOST'
919
                }
920
            );
921
            is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
922
            my $lost_fee_line = $lost_fee_lines->next;
923
            is( $lost_fee_line->amount + 0,
924
                $replacement_amount, 'The right LOST amount is generated' );
925
            is( $lost_fee_line->amountoutstanding + 0,
926
                $replacement_amount,
927
                'The right LOST amountountstanding is generated' );
928
929
            my $account = $patron->account;
930
            my $debts   = $account->outstanding_debits;
931
932
            # Pay off the debt
933
            my $credit = $account->add_credit(
934
                {
935
                    amount    => $account->balance,
936
                    type      => 'PAYMENT',
937
                    interface => 'test',
938
                }
939
            );
940
            $credit->apply(
941
                { debits => [ $debts->as_list ], offset_type => 'Payment' } );
942
943
            # Fine not forgiven
944
            my $overdue = $account->add_debit(
945
                {
946
                    amount     => 30.00,
947
                    user_id    => $manager->borrowernumber,
948
                    library_id => $library->branchcode,
949
                    interface  => 'test',
950
                    item_id    => $item->itemnumber,
951
                    type       => 'OVERDUE',
952
                }
953
            )->store();
954
            $overdue->status('LOST')->store();
955
            $overdue->discard_changes;
956
            is( $overdue->status, 'LOST',
957
                'Overdue status set to LOST' );
958
959
            # Simulate item marked as found
960
            $item->itemlost(0)->store;
961
            is( $item->{_refunded}, 1, 'Refund triggered' );
962
            is( $item->{_restored}, undef, 'Restore not triggered when overdue was not forgiven' );
963
            $overdue->discard_changes;
964
            is( $overdue->status, 'FOUND',
965
                'Overdue status updated to FOUND' );
966
        };
967
968
        subtest 'restore fine | forgiven overdue' => sub {
969
970
            plan tests => 12;
971
972
            # Set lostreturn_policy to 'restore' for tests
973
            my $manager =
974
              $builder->build_object( { class => "Koha::Patrons" } );
975
            t::lib::Mocks::mock_userenv(
976
                { patron => $manager, branchcode => $manager->branchcode } );
977
            my $specific_rule_restore = $builder->build(
978
                {
979
                    source => 'CirculationRule',
980
                    value  => {
981
                        branchcode   => $manager->branchcode,
982
                        categorycode => undef,
983
                        itemtype     => undef,
984
                        rule_name    => 'lostreturn',
985
                        rule_value   => 'restore'
986
                    }
987
                }
988
            );
989
990
            my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
991
992
            my $item = $builder->build_sample_item(
993
                {
994
                    biblionumber     => $biblio->biblionumber,
995
                    library          => $library->branchcode,
996
                    replacementprice => $replacement_amount,
997
                    itype            => $item_type->itemtype
998
                }
999
            );
1000
1001
            my $issue =
1002
              C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
1003
1004
            # Simulate item marked as lost
1005
            $item->itemlost(1)->store;
1006
            C4::Circulation::LostItem( $item->itemnumber, 1 );
1007
1008
            my $processing_fee_lines = Koha::Account::Lines->search(
1009
                {
1010
                    borrowernumber  => $patron->id,
1011
                    itemnumber      => $item->itemnumber,
1012
                    debit_type_code => 'PROCESSING'
1013
                }
1014
            );
1015
            is( $processing_fee_lines->count,
1016
                1, 'Only one processing fee produced' );
1017
            my $processing_fee_line = $processing_fee_lines->next;
1018
            is( $processing_fee_line->amount + 0,
1019
                $processfee_amount,
1020
                'The right PROCESSING amount is generated' );
1021
            is( $processing_fee_line->amountoutstanding + 0,
1022
                $processfee_amount,
1023
                'The right PROCESSING amountoutstanding is generated' );
1024
1025
            my $lost_fee_lines = Koha::Account::Lines->search(
1026
                {
1027
                    borrowernumber  => $patron->id,
1028
                    itemnumber      => $item->itemnumber,
1029
                    debit_type_code => 'LOST'
1030
                }
1031
            );
1032
            is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
1033
            my $lost_fee_line = $lost_fee_lines->next;
1034
            is( $lost_fee_line->amount + 0,
1035
                $replacement_amount, 'The right LOST amount is generated' );
1036
            is( $lost_fee_line->amountoutstanding + 0,
1037
                $replacement_amount,
1038
                'The right LOST amountountstanding is generated' );
1039
1040
            my $account = $patron->account;
1041
            my $debts   = $account->outstanding_debits;
1042
1043
            # Pay off the debt
1044
            my $credit = $account->add_credit(
1045
                {
1046
                    amount    => $account->balance,
1047
                    type      => 'PAYMENT',
1048
                    interface => 'test',
1049
                }
1050
            );
1051
            $credit->apply(
1052
                { debits => [ $debts->as_list ], offset_type => 'Payment' } );
1053
1054
            # Add overdue
1055
            my $overdue = $account->add_debit(
1056
                {
1057
                    amount     => 30.00,
1058
                    user_id    => $manager->borrowernumber,
1059
                    library_id => $library->branchcode,
1060
                    interface  => 'test',
1061
                    item_id    => $item->itemnumber,
1062
                    type       => 'OVERDUE',
1063
                }
1064
            )->store();
1065
            $overdue->status('LOST')->store();
1066
            is( $overdue->status, 'LOST',
1067
                'Overdue status set to LOST' );
1068
1069
            # Forgive fine
1070
            $credit = $account->add_credit(
1071
                {
1072
                    amount     => 30.00,
1073
                    user_id    => $manager->borrowernumber,
1074
                    library_id => $library->branchcode,
1075
                    interface  => 'test',
1076
                    type       => 'FORGIVEN',
1077
                    item_id    => $item->itemnumber
1078
                }
1079
            );
1080
            $credit->apply(
1081
                { debits => [$overdue], offset_type => 'Forgiven' } );
1082
1083
            # Simulate item marked as found
1084
            $item->itemlost(0)->store;
1085
            is( $item->{_refunded}, 1, 'Refund triggered' );
1086
            is( $item->{_restored}, 1, 'Restore triggered when overdue was forgiven' );
1087
            $overdue->discard_changes;
1088
            is( $overdue->status, 'FOUND', 'Overdue status updated to FOUND' );
1089
            is( $overdue->amountoutstanding, $overdue->amount, 'Overdue outstanding has been restored' );
1090
            $credit->discard_changes;
1091
            is( $credit->status, 'VOID', 'Overdue Forgival has been marked as VOID');
1092
        };
1093
764
        subtest 'Continue when userenv is not set' => sub {
1094
        subtest 'Continue when userenv is not set' => sub {
765
            plan tests => 1;
1095
            plan tests => 1;
766
1096
767
- 

Return to bug 23091