From e8cbd4c753ccaf6d83d497a9ef4842600ad1758d Mon Sep 17 00:00:00 2001
From: Tomas Cohen Arazi <tomascohen@theke.io>
Date: Tue, 26 Jun 2018 11:52:44 -0300
Subject: [PATCH] Bug 20997: Add Koha::Account::Line::apply method

This patch implements the ->apply() method for account lines.
It will only work on credit lines, and raise an exception otherwise.

It implements ->is_credit() as well.

To test:
- Apply this patch
- Run:
  $ kshell
 k$ prove t/db_dependent/Koha/Account/Lines.t
=> SUCCESS: Tests pass!
- Check all use cases are covered by the tests
- Sign off :-D

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

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
---
 Koha/Account/Line.pm       | 85 +++++++++++++++++++++++++++++++++++++-
 Koha/Exceptions/Account.pm | 66 +++++++++++++++++++++++++++++
 2 files changed, 149 insertions(+), 2 deletions(-)
 create mode 100644 Koha/Exceptions/Account.pm

diff --git a/Koha/Account/Line.pm b/Koha/Account/Line.pm
index caba957826..f339b37f13 100644
--- a/Koha/Account/Line.pm
+++ b/Koha/Account/Line.pm
@@ -22,9 +22,10 @@ use Data::Dumper;
 
 use C4::Log qw(logaction);
 
+use Koha::Account::Offsets;
 use Koha::Database;
+use Koha::Exceptions::Account;
 use Koha::Items;
-use Koha::Account::Offsets;
 
 use base qw(Koha::Object);
 
@@ -34,7 +35,7 @@ Koha::Account::Lines - Koha accountline Object class
 
 =head1 API
 
-=head2 Class Methods
+=head2 Class methods
 
 =cut
 
@@ -125,6 +126,86 @@ sub void {
 
 }
 
+=head3 apply
+
+    my $outstanding_amount = $credit->apply({ debit =>  $debit, [ offset_type => $offset_type ] });
+
+=cut
+
+sub apply {
+    my ( $self, $params ) = @_;
+
+    my $debit       = $params->{debit};
+    my $offset_type = $params->{offset_type} // 'credit_applied';
+
+    unless ( $self->is_credit ) {
+        Koha::Exceptions::Account::IsNotCredit->throw(
+            error => 'Account line ' . $self->id . ' is not a credit'
+        );
+    }
+
+    unless ( !$debit->is_credit ) {
+        Koha::Exceptions::Account::IsNotDebit->throw(
+            error => 'Account line ' . $debit->id . 'is not a debit'
+        );
+    }
+
+    my $available_credit = $self->amountoutstanding * -1;
+
+    unless ( $available_credit > 0 ) {
+        Koha::Exceptions::Account::NoAvailableCredit->throw(
+            error => 'Outstanding credit is ' . $available_credit . ' and cannot be applied'
+        );
+    }
+
+    my $schema = Koha::Database->new->schema;
+
+    $schema->txn_do( sub {
+
+        my $amount_to_cancel;
+        my $owed = $debit->amountoutstanding;
+
+        if ( $available_credit >= $owed ) {
+            $amount_to_cancel = $owed;
+        }
+        else {    # $available_credit < $debit->amountoutstanding
+            $amount_to_cancel = $available_credit;
+        }
+
+        # record the account offset
+        Koha::Account::Offset->new(
+            {   credit_id => $self->id,
+                debit_id  => $debit->id,
+                amount    => $amount_to_cancel,
+                type      => $offset_type,
+            }
+        )->store();
+
+        $available_credit -= $amount_to_cancel;
+
+        $self->amountoutstanding( $available_credit * -1 )->store;
+        $debit->amountoutstanding( $owed - $amount_to_cancel )->store;
+    });
+
+    return $available_credit;
+}
+
+=head3 is_credit
+
+    my $bool = $line->is_credit;
+
+=cut
+
+sub is_credit {
+    my ($self) = @_;
+
+    return ( $self->amount < 0 );
+}
+
+=head2 Internal methods
+
+=cut
+
 =head3 _type
 
 =cut
diff --git a/Koha/Exceptions/Account.pm b/Koha/Exceptions/Account.pm
new file mode 100644
index 0000000000..5cafe5f9ab
--- /dev/null
+++ b/Koha/Exceptions/Account.pm
@@ -0,0 +1,66 @@
+package Koha::Exceptions::Account;
+
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Exception::Class (
+
+    'Koha::Exceptions::Account' => {
+        description => 'Something went wrong!',
+    },
+    'Koha::Exceptions::Account::IsNotCredit' => {
+        isa => 'Koha::Exceptions::Account',
+        description => 'Account line is not a credit'
+    },
+    'Koha::Exceptions::Account::IsNotDebit' => {
+        isa => 'Koha::Exceptions::Account',
+        description => 'Account line is not a credit'
+    },
+    'Koha::Exceptions::Account::NoAvailableCredit' => {
+        isa => 'Koha::Exceptions::Account',
+        description => 'No outstanding credit'
+    }
+);
+
+=head1 NAME
+
+Koha::Exceptions::Account - Base class for Account exceptions
+
+=head1 Exceptions
+
+=head2 Koha::Exceptions::Account
+
+Generic Account exception
+
+=head2 Koha::Exceptions::Account::IsNotCredit
+
+Exception to be used when an action on an account line requires it to be a
+credit and it isn't.
+
+=head2 Koha::Exceptions::Account::IsNotDebit
+
+Exception to be used when an action on an account line requires it to be a
+debit and it isn't.
+
+=head2 Koha::Exceptions::Account::NoAvailableCredit
+
+Exception to be used when a credit has no amount outstanding and is required
+to be applied to outstanding debits.
+
+=cut
+
+1;
-- 
2.18.0