Bug 13098: Tests for all _FixAccountForLostAndReturned use cases
authorTomas Cohen Arazi <tomascohen@theke.io>
Fri, 12 Oct 2018 13:27:06 +0000 (10:27 -0300)
committerNick Clemens <nick@bywatersolutions.com>
Wed, 31 Oct 2018 13:12:25 +0000 (13:12 +0000)
This patch introduces tests for all the use cases for
_FixAccountForLostAndReturned. This includes the following scenarios
before _FixAccountForLostAndReturned is called:

- Full write off (the original issue)
- Full payment
- No payment or write off
- Partial payment and partial write off

Written off debts (L lines) are just skipped, any kind of payment is
added and then refund as a single credit (CR). Amount outstanding is set
to 0. Offsets need to be generated for paper trail of this decisions.

To test:
- Run:
  $ kshell
 k$ prove t/db_dependent/Circulation.t
=> FAIL: Tests fail because _FixAccountForLostAndReturned is not doing
the right thing :-D

Followed test plan, patch works as described
Signed-off-by: Alex Buckley <alexbuckley@catalyst.net.nz>

Signed-off-by: Josef Moravec <josef.moravec@gmail.com>

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>

t/db_dependent/Circulation.t

index 817461b..7cc6c83 100755 (executable)
@@ -1982,54 +1982,303 @@ subtest 'AddReturn | is_overdue' => sub {
 };
 
 subtest '_FixAccountForLostAndReturned' => sub {
-    plan tests => 2;
 
-    # Generate test biblio
-    my $title  = 'Koha for Dummies';
-    my ( $biblionumber, $biblioitemnumber ) = add_biblio($title, 'Hall, Daria');
+    plan tests => 4;
 
-    my $barcode = 'KD123456789';
-    my $branchcode  = $library2->{branchcode};
+    t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
+    t::lib::Mocks::mock_preference( 'WhenLostForgiveFine',          0 );
 
-    my ( $item_bibnum, $item_bibitemnum, $itemnumber ) = AddItem(
-        {
-            homebranch       => $branchcode,
-            holdingbranch    => $branchcode,
-            barcode          => $barcode,
-            replacementprice => 99.00,
-            itype            => $itemtype
-        },
-        $biblionumber
+    my $processfee_amount  = 20.00;
+    my $replacement_amount = 99.00;
+    my $item_type          = $builder->build_object(
+        {   class => 'Koha::ItemTypes',
+            value => {
+                notforloan         => undef,
+                rentalcharge       => 0,
+                defaultreplacecost => undef,
+                processfee         => $processfee_amount
+            }
+        }
     );
+    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
 
-    my $patron = $builder->build( { source => 'Borrower' } );
+    # Generate test biblio
+    my $title = 'Koha for Dummies';
+    my ( $biblionumber, $biblioitemnumber ) = add_biblio( $title, 'Hall, Daria' );
 
-    Koha::Account::Line->new(
-        {
-            borrowernumber => $patron->{borrowernumber},
-            accounttype    => 'F',
-            itemnumber     => $itemnumber,
-            amount => 10.00,
-            amountoutstanding => 10.00,
-        }
-    )->store();
+    subtest 'Full write-off tests' => sub {
 
-    my $accountline = Koha::Account::Line->new(
-        {
-            borrowernumber => $patron->{borrowernumber},
-            accounttype    => 'L',
-            itemnumber     => $itemnumber,
-            amount => 99.00,
-            amountoutstanding => 99.00,
-        }
-    )->store();
+        plan tests => 10;
 
-    C4::Circulation::_FixAccountForLostAndReturned( $itemnumber, $patron->{borrowernumber} );
+        my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
+        my $barcode = 'KD123456789';
 
-    $accountline->_result()->discard_changes();
+        my ( undef, undef, $item_id ) = AddItem(
+            {   homebranch       => $library->branchcode,
+                holdingbranch    => $library->branchcode,
+                barcode          => $barcode,
+                replacementprice => $replacement_amount,
+                itype            => $item_type->itemtype
+            },
+            $biblionumber
+        );
+
+        AddIssue( $patron->unblessed, $barcode );
+
+        # Simulate item marked as lost
+        ModItem( { itemlost => 3 }, $biblionumber, $item_id );
+        LostItem( $item_id, 1 );
+
+        my $processing_fee_lines = Koha::Account::Lines->search(
+            { borrowernumber => $patron->id, itemnumber => $item_id, accounttype => 'PF' } );
+        is( $processing_fee_lines->count, 1, 'Only one processing fee produced' );
+        my $processing_fee_line = $processing_fee_lines->next;
+        is( $processing_fee_line->amount + 0.00,
+            $processfee_amount, 'The right PF amount is generated' );
+        is( $processing_fee_line->amountoutstanding + 0.00,
+            $processfee_amount, 'The right PF amountoutstanding is generated' );
+
+        my $lost_fee_lines = Koha::Account::Lines->search(
+            { borrowernumber => $patron->id, itemnumber => $item_id, accounttype => 'L' } );
+        is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
+        my $lost_fee_line = $lost_fee_lines->next;
+        is( $lost_fee_line->amount + 0.00, $replacement_amount, 'The right L amount is generated' );
+        is( $lost_fee_line->amountoutstanding + 0.00,
+            $replacement_amount, 'The right L amountountstanding is generated' );
+
+        my $account = $patron->account;
+        my $debts   = $account->outstanding_debits;
+
+        # Write off the debt
+        my $credit = $account->add_credit(
+            {   amount => $account->balance,
+                type   => 'writeoff'
+            }
+        );
+        $credit->apply( { debits => $debts, offset_type => 'Writeoff' } );
+
+        my $credit_return_id = C4::Circulation::_FixAccountForLostAndReturned( $item_id, $patron->id );
+        is( $credit_return_id, undef, 'No CR account line added' );
+
+        $lost_fee_line->discard_changes; # reload from DB
+        is( $lost_fee_line->amountoutstanding + 0.00, 0.00, 'Lost fee has no outstanding amount' );
+        is( $lost_fee_line->accounttype,
+            'LR', 'Lost fee now has account type of LR ( Lost Returned )' );
+
+        is( $patron->account->balance, -0, 'The patron balance is 0, everything was written off' );
+    };
 
-    is( $accountline->amountoutstanding, '0.000000', 'Lost fee has no outstanding amount' );
-    is( $accountline->accounttype, 'LR', 'Lost fee now has account type of LR ( Lost Returned )');
+    subtest 'Full payment tests' => sub {
+
+        plan tests => 12;
+
+        my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
+        my $barcode = 'KD123456790';
+
+        my ( undef, undef, $item_id ) = AddItem(
+            {   homebranch       => $library->branchcode,
+                holdingbranch    => $library->branchcode,
+                barcode          => $barcode,
+                replacementprice => $replacement_amount,
+                itype            => $item_type->itemtype
+            },
+            $biblionumber
+        );
+
+        AddIssue( $patron->unblessed, $barcode );
+
+        # Simulate item marked as lost
+        ModItem( { itemlost => 1 }, $biblionumber, $item_id );
+        LostItem( $item_id, 1 );
+
+        my $processing_fee_lines = Koha::Account::Lines->search(
+            { borrowernumber => $patron->id, itemnumber => $item_id, accounttype => 'PF' } );
+        is( $processing_fee_lines->count, 1, 'Only one processing fee produced' );
+        my $processing_fee_line = $processing_fee_lines->next;
+        is( $processing_fee_line->amount + 0.00,
+            $processfee_amount, 'The right PF amount is generated' );
+        is( $processing_fee_line->amountoutstanding + 0.00,
+            $processfee_amount, 'The right PF amountoutstanding is generated' );
+
+        my $lost_fee_lines = Koha::Account::Lines->search(
+            { borrowernumber => $patron->id, itemnumber => $item_id, accounttype => 'L' } );
+        is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
+        my $lost_fee_line = $lost_fee_lines->next;
+        is( $lost_fee_line->amount + 0.00, $replacement_amount, 'The right L amount is generated' );
+        is( $lost_fee_line->amountoutstanding + 0.00,
+            $replacement_amount, 'The right L amountountstanding is generated' );
+
+        my $account = $patron->account;
+        my $debts   = $account->outstanding_debits;
+
+        # Write off the debt
+        my $credit = $account->add_credit(
+            {   amount => $account->balance,
+                type   => 'payment'
+            }
+        );
+        $credit->apply( { debits => $debts, offset_type => 'Payment' } );
+
+        my $credit_return_id = C4::Circulation::_FixAccountForLostAndReturned( $item_id, $patron->id );
+        my $credit_return = Koha::Account::Lines->find($credit_return_id);
+
+        is( $credit_return->accounttype, 'CR', 'An account line of type CR is added' );
+        is( $credit_return->amount + 0.00,
+            -99.00, 'The account line of type CR has an amount of -99' );
+        is( $credit_return->amountoutstanding + 0.00,
+            -99.00, 'The account line of type CR has an amountoutstanding of -99' );
+
+        $lost_fee_line->discard_changes;
+        is( $lost_fee_line->amountoutstanding + 0.00, 0.00, 'Lost fee has no outstanding amount' );
+        is( $lost_fee_line->accounttype,
+            'LR', 'Lost fee now has account type of LR ( Lost Returned )' );
+
+        is( $patron->account->balance,
+            -99, 'The patron balance is -99, a credit that equals the lost fee payment' );
+    };
+
+    subtest 'Test without payment or write off' => sub {
+
+        plan tests => 10;
+
+        my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
+        my $barcode = 'KD123456791';
+
+        my ( undef, undef, $item_id ) = AddItem(
+            {   homebranch       => $library->branchcode,
+                holdingbranch    => $library->branchcode,
+                barcode          => $barcode,
+                replacementprice => $replacement_amount,
+                itype            => $item_type->itemtype
+            },
+            $biblionumber
+        );
+
+        AddIssue( $patron->unblessed, $barcode );
+
+        # Simulate item marked as lost
+        ModItem( { itemlost => 3 }, $biblionumber, $item_id );
+        LostItem( $item_id, 1 );
+
+        my $processing_fee_lines = Koha::Account::Lines->search(
+            { borrowernumber => $patron->id, itemnumber => $item_id, accounttype => 'PF' } );
+        is( $processing_fee_lines->count, 1, 'Only one processing fee produced' );
+        my $processing_fee_line = $processing_fee_lines->next;
+        is( $processing_fee_line->amount + 0.00,
+            $processfee_amount, 'The right PF amount is generated' );
+        is( $processing_fee_line->amountoutstanding + 0.00,
+            $processfee_amount, 'The right PF amountoutstanding is generated' );
+
+        my $lost_fee_lines = Koha::Account::Lines->search(
+            { borrowernumber => $patron->id, itemnumber => $item_id, accounttype => 'L' } );
+        is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
+        my $lost_fee_line = $lost_fee_lines->next;
+        is( $lost_fee_line->amount + 0.00, $replacement_amount, 'The right L amount is generated' );
+        is( $lost_fee_line->amountoutstanding + 0.00,
+            $replacement_amount, 'The right L amountountstanding is generated' );
+
+        my $credit_return_id = C4::Circulation::_FixAccountForLostAndReturned( $item_id, $patron->id );
+        is( $credit_return_id, undef, 'No CR account line added' );
+
+        $lost_fee_line->discard_changes;
+        is( $lost_fee_line->amountoutstanding + 0.00, 0.00, 'Lost fee has no outstanding amount' );
+        is( $lost_fee_line->accounttype, 'LR', 'Lost fee now has account type of LR ( Lost Returned )' );
+
+        is( $patron->account->balance, 20, 'The patron balance is 20, still owes the processing fee' );
+    };
+
+    subtest 'Test with partial payement and write off, and remaining debt' => sub {
+
+        plan tests => 15;
+
+        my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
+        my $barcode = 'KD123456792';
+
+        my ( undef, undef, $item_id ) = AddItem(
+            {   homebranch       => $library->branchcode,
+                holdingbranch    => $library->branchcode,
+                barcode          => $barcode,
+                replacementprice => $replacement_amount,
+                itype            => $item_type->itemtype
+            },
+            $biblionumber
+        );
+
+        AddIssue( $patron->unblessed, $barcode );
+
+        # Simulate item marked as lost
+        ModItem( { itemlost => 1 }, $biblionumber, $item_id );
+        LostItem( $item_id, 1 );
+
+        my $processing_fee_lines = Koha::Account::Lines->search(
+            { borrowernumber => $patron->id, itemnumber => $item_id, accounttype => 'PF' } );
+        is( $processing_fee_lines->count, 1, 'Only one processing fee produced' );
+        my $processing_fee_line = $processing_fee_lines->next;
+        is( $processing_fee_line->amount + 0.00,
+            $processfee_amount, 'The right PF amount is generated' );
+        is( $processing_fee_line->amountoutstanding + 0.00,
+            $processfee_amount, 'The right PF amountoutstanding is generated' );
+
+        my $lost_fee_lines = Koha::Account::Lines->search(
+            { borrowernumber => $patron->id, itemnumber => $item_id, accounttype => 'L' } );
+        is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
+        my $lost_fee_line = $lost_fee_lines->next;
+        is( $lost_fee_line->amount + 0.00, $replacement_amount, 'The right L amount is generated' );
+        is( $lost_fee_line->amountoutstanding + 0.00,
+            $replacement_amount, 'The right L amountountstanding is generated' );
+
+        my $account = $patron->account;
+        is( $account->balance, $processfee_amount + $replacement_amount, 'Balance is PF + L' );
+
+        # Partially pay fee
+        my $payment_amount = 27;
+        my $payment        = $account->add_credit(
+            {   amount => $payment_amount,
+                type   => 'payment'
+            }
+        );
+
+        $payment->apply( { debits => $lost_fee_lines->reset, offset_type => 'Payment' } );
+
+        # Partially write off fee
+        my $write_off_amount = 25;
+        my $write_off        = $account->add_credit(
+            {   amount => $write_off_amount,
+                type   => 'writeoff'
+            }
+        );
+        $write_off->apply( { debits => $lost_fee_lines->reset, offset_type => 'Writeoff' } );
+
+        is( $account->balance,
+            $processfee_amount + $replacement_amount - $payment_amount - $write_off_amount,
+            'Payment and write off applied'
+        );
+
+        my $credit_return_id = C4::Circulation::_FixAccountForLostAndReturned( $item_id, $patron->id );
+        my $credit_return = Koha::Account::Lines->find($credit_return_id);
+
+        is( $account->balance, $processfee_amount - $payment_amount, 'Balance is PF - payment (CR)' );
+
+        is( $credit_return->accounttype, 'CR', 'An account line of type CR is added' );
+        is( $credit_return->amount + 0.00,
+            $payment_amount * -1,
+            'The account line of type CR has an amount equal to the payment'
+        );
+        is( $credit_return->amountoutstanding + 0.00,
+            $payment_amount * -1,
+            'The account line of type CR has an amountoutstanding equal to the payment'
+        );
+
+        $lost_fee_line->discard_changes;
+        is( $lost_fee_line->amountoutstanding + 0.00, 0.00, 'Lost fee has no outstanding amount' );
+        is( $lost_fee_line->accounttype,
+            'LR', 'Lost fee now has account type of LR ( Lost Returned )' );
+
+        is( $account->balance,
+            $processfee_amount - $payment_amount,
+            'The patron balance is the difference between the PF and the credit'
+        );
+    };
 };
 
 subtest '_FixOverduesOnReturn' => sub {