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

(-)a/Koha/Account/Line.pm (-9 / +26 lines)
Lines 848-862 sub adjust { Link Here
848
848
849
            # Catch cases that require patron refunds
849
            # Catch cases that require patron refunds
850
            if ( $new_outstanding < 0 ) {
850
            if ( $new_outstanding < 0 ) {
851
                my $account = Koha::Patrons->find( $self->borrowernumber )->account;
851
852
                my $credit  = $account->add_credit(
852
                # Only generate an OVERPAYMENT credit for reversible payments
853
                    {
853
                # (e.g. cash payments). Non-reversible credits such as writeoffs,
854
                        amount    => $new_outstanding * -1,
854
                # discounts and cancellations should simply be absorbed when the
855
                        type      => 'OVERPAYMENT',
855
                # fine is adjusted downward - they must not produce a refund
856
                        interface => $interface,
856
                # credit for the patron.
857
                        ( $update_type eq 'overdue_update' ? ( item_id => $self->itemnumber ) : () ),
857
                my $total_non_reversible = 0;
858
                    }
858
                if ( my $debit_offsets = $self->debit_offsets ) {
859
                );
859
                    my $nr_offsets = $debit_offsets->filter_by_non_reversible;
860
                    $total_non_reversible += abs( $_->amount ) for $nr_offsets->as_list;
861
                }
862
                my $total_applied      = $amount_before - $amount_outstanding_before;
863
                my $total_reversible   = $total_applied - $total_non_reversible;
864
                my $overpayment_amount = $total_reversible - $amount;
865
866
                if ( $overpayment_amount > 0 ) {
867
                    my $account = Koha::Patrons->find( $self->borrowernumber )->account;
868
                    $account->add_credit(
869
                        {
870
                            amount    => $overpayment_amount,
871
                            type      => 'OVERPAYMENT',
872
                            interface => $interface,
873
                            ( $update_type eq 'overdue_update' ? ( item_id => $self->itemnumber ) : () ),
874
                        }
875
                    );
876
                }
860
                $new_outstanding = 0;
877
                $new_outstanding = 0;
861
            }
878
            }
862
879
(-)a/t/db_dependent/Koha/Account/Line.t (-2 / +107 lines)
Lines 20-26 Link Here
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::NoWarnings;
22
use Test::NoWarnings;
23
use Test::More tests => 16;
23
use Test::More tests => 17;
24
use Test::Exception;
24
use Test::Exception;
25
use Test::MockModule;
25
use Test::MockModule;
26
26
Lines 726-731 subtest 'adjust() tests' => sub { Link Here
726
    $schema->storage->txn_rollback;
726
    $schema->storage->txn_rollback;
727
};
727
};
728
728
729
subtest 'adjust() tests (writeoff does not generate overpayment credit)' => sub {
730
731
    plan tests => 12;
732
733
    $schema->storage->txn_begin;
734
735
    my $patron  = $builder->build_object( { class => 'Koha::Patrons' } );
736
    my $account = $patron->account;
737
738
    # Scenario 1: Full writeoff then fine adjusted to 0 - no OVERPAYMENT credit
739
    my $debit_1 = Koha::Account::Line->new(
740
        {
741
            borrowernumber    => $patron->id,
742
            debit_type_code   => "OVERDUE",
743
            status            => "UNRETURNED",
744
            amount            => 100,
745
            amountoutstanding => 100,
746
            interface         => 'commandline',
747
        }
748
    )->store;
749
750
    my $lines_count_before = $account->lines->count;
751
752
    # Write off the full fine
753
    $account->pay(
754
        {
755
            amount    => 100,
756
            type      => 'WRITEOFF',
757
            lines     => [$debit_1],
758
            interface => 'commandline',
759
        }
760
    );
761
    $debit_1->discard_changes;
762
    is( $debit_1->amountoutstanding * 1, 0, 'Fine fully written off' );
763
764
    # Simulate check-in recalculating fine to 0 (backdated or fine-free)
765
    $debit_1->adjust( { amount => 0, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
766
767
    is( $debit_1->amount * 1,            0, 'Fine amount adjusted to 0' );
768
    is( $debit_1->amountoutstanding * 1, 0, 'Fine amountoutstanding remains 0' );
769
770
    # No new OVERPAYMENT credit should have been generated
771
    is(
772
        $account->lines->count,
773
        $lines_count_before + 1,    # only the WRITEOFF credit, no OVERPAYMENT
774
        'No OVERPAYMENT credit generated when fine adjusted after writeoff'
775
    );
776
    isnt(
777
        $account->lines->last->credit_type_code,
778
        'OVERPAYMENT',
779
        'Last account line is not an OVERPAYMENT credit'
780
    );
781
782
    # Scenario 2: Partial payment + partial writeoff, fine drops below payment amount
783
    # => OVERPAYMENT only for the payment portion
784
    my $debit_2 = Koha::Account::Line->new(
785
        {
786
            borrowernumber    => $patron->id,
787
            debit_type_code   => "OVERDUE",
788
            status            => "UNRETURNED",
789
            amount            => 100,
790
            amountoutstanding => 100,
791
            interface         => 'commandline',
792
        }
793
    )->store;
794
795
    # Apply a $60 writeoff
796
    $account->pay(
797
        {
798
            amount    => 60,
799
            type      => 'WRITEOFF',
800
            lines     => [$debit_2],
801
            interface => 'commandline',
802
        }
803
    );
804
    $debit_2->discard_changes;
805
    is( $debit_2->amountoutstanding * 1, 40, 'Fine outstanding after writeoff' );
806
807
    # Apply a $30 cash payment
808
    my $payment = $account->add_credit( { amount => 30, type => 'PAYMENT', interface => 'commandline' } );
809
    my $debits  = Koha::Account::Lines->search( { accountlines_id => $debit_2->id } );
810
    $payment->apply( { debits => [ $debits->as_list ] } );
811
    $debit_2->discard_changes;
812
    is( $debit_2->amountoutstanding * 1, 10, 'Fine outstanding after payment' );
813
814
    my $lines_count_before_2 = $account->lines->count;
815
816
    # Simulate check-in recalculating fine to $0 (below the $30 payment)
817
    $debit_2->adjust( { amount => 0, type => 'overdue_update', interface => 'commandline' } )->discard_changes;
818
819
    is( $debit_2->amount * 1,            0, 'Fine amount adjusted to 0' );
820
    is( $debit_2->amountoutstanding * 1, 0, 'Fine amountoutstanding remains 0' );
821
822
    # OVERPAYMENT credit should be generated only for the reversible payment amount ($30)
823
    is(
824
        $account->lines->count,
825
        $lines_count_before_2 + 1,
826
        'One OVERPAYMENT credit generated (for cash payment only)'
827
    );
828
    my $overpayment = $account->lines->last;
829
    is( $overpayment->amount * 1,       -30,           'OVERPAYMENT credit is for payment amount only, not writeoff' );
830
    is( $overpayment->credit_type_code, 'OVERPAYMENT', 'Credit type is OVERPAYMENT' );
831
832
    $schema->storage->txn_rollback;
833
};
834
729
subtest 'checkout() tests' => sub {
835
subtest 'checkout() tests' => sub {
730
    plan tests => 7;
836
    plan tests => 7;
731
837
732
- 

Return to bug 29923