From adc41b08c0f5b8f60fef21a7292c307db31b4f7d Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Fri, 5 Feb 2021 12:56:43 +0000 Subject: [PATCH] Bug 27636: Add payin_amount to Koha::Account This patch adds a `payin_amount` method to the Koha::Account as a modern replacement to the `pay` method. The new method uses the `add_credit` and `Koha::Account::Line->apply` methods internally for consistency and returns the created credit Koha::Account::Line result. Test plan 1/ Run t/db_dependent/Koha/Account.t --- Koha/Account.pm | 89 +++++++++++++++++++++++ t/db_dependent/Koha/Account.t | 129 +++++++++++++++++++++++++++++++++- 2 files changed, 217 insertions(+), 1 deletion(-) diff --git a/Koha/Account.pm b/Koha/Account.pm index e9e54f03db..908f1e1bba 100644 --- a/Koha/Account.pm +++ b/Koha/Account.pm @@ -485,6 +485,95 @@ sub add_credit { return $line; } +=head3 payin_amount + + my $credit = $account->payin_amount( + { + amount => $amount, + credit_type => $credit_type, + payment_type => $payment_type, + cash_register => $register_id, + interface => $interface, + library_id => $branchcode, + user_id => $staff_id, + debits => $debit_lines, + description => $description, + note => $note + } + ); + +This method allows an amount to be paid into a patrons account and immediately applied against debts. + +You can optionally pass a debts parameter which consists of an arrayref of Koha::Account::Line debit lines. + +$credit_type can be any of: + - 'PAYMENT' + - 'WRITEOFF' + - 'FORGIVEN' + +=cut + +sub payin_amount { + my ( $self, $params ) = @_; + + # check for mandatory params + my @mandatory = ( 'interface', 'amount', 'type' ); + for my $param (@mandatory) { + unless ( defined( $params->{$param} ) ) { + Koha::Exceptions::MissingParameter->throw( + error => "The $param parameter is mandatory" ); + } + } + + # Check for mandatory register + Koha::Exceptions::Account::RegisterRequired->throw() + if ( C4::Context->preference("UseCashRegisters") + && defined( $params->{payment_type} ) + && ( $params->{payment_type} eq 'CASH' ) + && !defined($params->{cash_register}) ); + + # amount should always be passed as a positive value + my $amount = $params->{amount}; + unless ( $amount > 0 ) { + Koha::Exceptions::Account::AmountNotPositive->throw( + error => 'Payin amount passed is not positive' ); + } + + my $credit; + my $schema = Koha::Database->new->schema; + $schema->txn_do( + sub { + + # Add payin credit + $credit = $self->add_credit($params); + + # Offset debts passed first + if ( exists( $params->{debits} ) ) { + $credit = $credit->apply( + { + debits => $params->{debits}, + offset_type => $params->{type} + } + ); + } + + # Offset against remaining balance if AutoReconcile + if ( C4::Context->preference("AccountAutoReconcile") + && $credit->amountoutstanding != 0 ) + { + $credit = $credit->apply( + { + debits => [ $self->outstanding_debits->as_list ], + offset_type => $params->{type} + } + ); + } + } + ); + + return $credit; +} + =head3 add_debit This method allows adding debits to a patron's account diff --git a/t/db_dependent/Koha/Account.t b/t/db_dependent/Koha/Account.t index 1b65341e59..eeea99cc42 100755 --- a/t/db_dependent/Koha/Account.t +++ b/t/db_dependent/Koha/Account.t @@ -19,7 +19,7 @@ use Modern::Perl; -use Test::More tests => 14; +use Test::More tests => 15; use Test::MockModule; use Test::Exception; @@ -1272,3 +1272,130 @@ subtest 'Koha::Account::payout_amount() tests' => sub { $schema->storage->txn_rollback; }; + +subtest 'Koha::Account::payin_amount() tests' => sub { + plan tests => 36; + + $schema->storage->txn_begin; + + # delete logs and statistics + my $action_logs = $schema->resultset('ActionLog')->search()->count; + my $statistics = $schema->resultset('Statistic')->search()->count; + + my $staff = $builder->build_object( { class => 'Koha::Patrons' } ); + my $library = $builder->build_object( { class => 'Koha::Libraries' } ); + my $register = + $builder->build_object( { class => 'Koha::Cash::Registers' } ); + my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); + my $account = + Koha::Account->new( { patron_id => $patron->borrowernumber } ); + + is( $account->balance, 0, 'Test patron has no balance' ); + + my $payin_params = { + type => 'PAYMENT', + payment_type => 'CASH', + branch => $library->id, + register_id => $register->id, + staff_id => $staff->id, + interface => 'intranet', + amount => -10, + }; + + my @required_fields = + ( 'interface', 'amount', 'type' ); + for my $required_field (@required_fields) { + my $this_payin = { %{$payin_params} }; + delete $this_payin->{$required_field}; + + throws_ok { + $account->payin_amount($this_payin); + } + 'Koha::Exceptions::MissingParameter', + "Exception thrown if $required_field parameter missing"; + } + + throws_ok { + $account->payin_amount($payin_params); + } + 'Koha::Exceptions::Account::AmountNotPositive', + 'Expected validation exception thrown (amount not positive)'; + + $payin_params->{amount} = 10; + + # Enable cash registers + t::lib::Mocks::mock_preference( 'UseCashRegisters', 1 ); + throws_ok { + $account->payin_amount($payin_params); + } + 'Koha::Exceptions::Account::RegisterRequired', +'Exception thrown for UseCashRegisters:1 + payment_type:CASH + cash_register:undef'; + + # Disable cash registers + t::lib::Mocks::mock_preference( 'UseCashRegisters', 0 ); + + # Enable AccountAutoReconcile + t::lib::Mocks::mock_preference( 'AccountAutoReconcile', 1 ); + + # Add some outstanding debits + my $debit_1 = $account->add_debit( { amount => 2, interface => 'commandline', type => 'OVERDUE' } ); + my $debit_2 = $account->add_debit( { amount => 3, interface => 'commandline', type => 'OVERDUE' } ); + my $debit_3 = $account->add_debit( { amount => 5, interface => 'commandline', type => 'OVERDUE' } ); + my $debit_4 = $account->add_debit( { amount => 10, interface => 'commandline', type => 'OVERDUE' } ); + my $debits = $account->outstanding_debits(); + is( $debits->count, 4, "Found 4 debits with outstanding amounts" ); + is( $debits->total_outstanding + 0, 20, "Total 20 outstanding debit" ); + + my $payin = $account->payin_amount($payin_params); + is(ref($payin), 'Koha::Account::Line', 'Return the Koha::Account::Line object for the payin'); + is($payin->amount + 0, -10, "Payin amount recorded correctly"); + is($payin->amountoutstanding + 0, 0, "Full amount was used to pay debts"); + $debits = $account->outstanding_debits(); + is($debits->count, 1, "Payin was applied against oldest outstanding debits first"); + is($debits->total_outstanding + 0, 10, "Total of 10 outstanding debit remaining"); + + my $offsets = Koha::Account::Offsets->search( { credit_id => $payin->id } ); + is( $offsets->count, 4, 'Four offsets generated' ); + my $offset = $offsets->next; + is( $offset->type, 'Payment', 'Payment offset added for payin line' ); + is( $offset->amount * 1, -10, 'Correct offset amount recorded' ); + $offset = $offsets->next; + is( $offset->debit_id, $debit_1->id, "Offset added against debit_1"); + is( $offset->type, 'PAYMENT', "Payment used for offset_type" ); + is( $offset->amount * 1, -2, 'Correct amount offset against debit_1' ); + $offset = $offsets->next; + is( $offset->debit_id, $debit_2->id, "Offset added against debit_2"); + is( $offset->type, 'PAYMENT', "Payment used for offset_type" ); + is( $offset->amount * 1, -3, 'Correct amount offset against debit_2' ); + $offset = $offsets->next; + is( $offset->debit_id, $debit_3->id, "Offset added against debit_3"); + is( $offset->type, 'PAYMENT', "Payment used for offset_type" ); + is( $offset->amount * 1, -5, 'Correct amount offset against debit_3' ); + + my $debit_5 = $account->add_debit( { amount => 5, interface => 'commandline', type => 'OVERDUE' } ); + $debits = $account->outstanding_debits(); + is($debits->count, 2, "New debit added"); + $payin_params->{amount} = 2.50; + $payin_params->{debits} = [$debit_5]; + $payin = $account->payin_amount($payin_params); + + $debits = $account->outstanding_debits(); + is($debits->count, 2, "Second debit not fully paid off"); + is($debits->total_outstanding + 0, 12.50, "12.50 outstanding debit remaining"); + $debit_4->discard_changes; + $debit_5->discard_changes; + is($debit_4->amountoutstanding + 0, 10, "Debit 4 unaffected when debit_5 was passed to payin_amount"); + is($debit_5->amountoutstanding + 0, 2.50, "Debit 5 correctly reduced when payin_amount called with debit_5 passed"); + + $offsets = Koha::Account::Offsets->search( { credit_id => $payin->id } ); + is( $offsets->count, 2, 'Two offsets generated' ); + $offset = $offsets->next; + is( $offset->type, 'Payment', 'Payment offset added for payin line' ); + is( $offset->amount * 1, -2.50, 'Correct offset amount recorded' ); + $offset = $offsets->next; + is( $offset->debit_id, $debit_5->id, "Offset added against debit_5"); + is( $offset->type, 'PAYMENT', "PAYMENT used for offset_type" ); + is( $offset->amount * 1, -2.50, 'Correct amount offset against debit_5' ); + + $schema->storage->txn_rollback; +}; -- 2.20.1