From 0c6d39530c1bbe4c13724e6bbe8709f0b4587fac Mon Sep 17 00:00:00 2001
From: Martin Renvoize <martin.renvoize@ptfs-europe.com>
Date: Tue, 17 Dec 2019 15:58:21 +0000
Subject: [PATCH] Bug 24255: Add summation methods to Koha::Account::Lines

This patch adds a number of summation methods to Koha::Account::Lines
giving quick access to overall total, total credits and total debits.

Test plan
1) Run the included tests
---
 Koha/Account/Lines.pm               |  82 +++++++++-
 t/db_dependent/Koha/Account/Lines.t | 230 +++++++++++++++++++++++++++-
 2 files changed, 309 insertions(+), 3 deletions(-)

diff --git a/Koha/Account/Lines.pm b/Koha/Account/Lines.pm
index 75225fcfeb..07c6f0b24b 100644
--- a/Koha/Account/Lines.pm
+++ b/Koha/Account/Lines.pm
@@ -43,13 +43,13 @@ empty it returns 0.
 =cut
 
 sub total_outstanding {
-    my ( $self ) = @_;
+    my ($self) = @_;
 
     my $lines = $self->search(
         {},
         {
             select => [ { sum => 'amountoutstanding' } ],
-            as => ['total_amountoutstanding'],
+            as     => ['total_amountoutstanding'],
         }
     );
 
@@ -58,6 +58,84 @@ sub total_outstanding {
       : 0;
 }
 
+=head3 total
+
+    my $lines = Koha::Account::Lines->search({ ...  });
+    my $total = $lines->total;
+
+Returns the sum of the amounts of the resultset. If the resultset is
+empty it returns 0.
+
+=cut
+
+sub total {
+    my ( $self, $conditions ) = @_;
+
+    $conditions //= {};
+    my $lines = $self->search(
+        $conditions,
+        {
+            select => [ { sum => 'me.amount' } ],
+            as     => ['total']
+        }
+    );
+    return $lines->count ? $lines->next->get_column('total') + 0 : 0;
+}
+
+=head3 credits_total
+
+    my $lines = Koha::Account::Lines->search({ ...  });
+    my $credits_total = $lines->credits_total;
+
+Returns the sum of the amounts of the resultset. If the resultset is
+empty it returns 0.
+
+=cut
+
+sub credits_total {
+    my ( $self, $conditions ) = @_;
+
+    my $local_conditions = { 'me.amount' => { '<' => 0 } };
+    $conditions //= {};
+    my $merged_conditions = { %{$conditions}, %{$local_conditions} };
+
+    my $lines = $self->search(
+        $merged_conditions,
+        {
+            select => [ { sum => 'me.amount' } ],
+            as     => ['total']
+        }
+    );
+    return $lines->count ? $lines->next->get_column('total') + 0 : 0;
+}
+
+=head3 debits_total
+
+    my $lines = Koha::Account::Lines->search({ ...  });
+    my $debits_total = $lines->debits_total;
+
+Returns the sum of the amounts of the resultset. If the resultset is
+empty it returns 0.
+
+=cut
+
+sub debits_total {
+    my ( $self, $conditions ) = @_;
+
+    my $local_conditions = { 'me.amount' => { '>' => 0 } };
+    $conditions //= {};
+    my $merged_conditions = { %{$conditions}, %{$local_conditions} };
+
+    my $lines = $self->search(
+        $merged_conditions,
+        {
+            select => [ { sum => 'me.amount' } ],
+            as     => ['total']
+        }
+    );
+    return $lines->count ? $lines->next->get_column('total') + 0 : 0;
+}
+
 =head2 Internal methods
 
 =head3 _type
diff --git a/t/db_dependent/Koha/Account/Lines.t b/t/db_dependent/Koha/Account/Lines.t
index 6e99bb45eb..f0601354f8 100755
--- a/t/db_dependent/Koha/Account/Lines.t
+++ b/t/db_dependent/Koha/Account/Lines.t
@@ -19,7 +19,7 @@
 
 use Modern::Perl;
 
-use Test::More tests => 11;
+use Test::More tests => 14;
 use Test::Exception;
 
 use C4::Circulation qw/AddIssue AddReturn/;
@@ -178,6 +178,234 @@ subtest 'total_outstanding() tests' => sub {
     $schema->storage->txn_rollback;
 };
 
+subtest 'total() tests' => sub {
+
+    plan tests => 5;
+
+    $schema->storage->txn_begin;
+
+    my $patron  = $builder->build_object({ class => 'Koha::Patrons' });
+
+    my $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->total, 0, 'total returns 0 if no lines (undef case)' );
+
+    my $debit_1 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => 10,
+            amountoutstanding => 10,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    my $debit_2 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code       => "OVERDUE",
+            status            => "RETURNED",
+            amount            => 10,
+            amountoutstanding => 10,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->total, 20, 'total sums correctly' );
+
+    my $credit_1 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => -10,
+            amountoutstanding => -10,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->total, 10, 'total sums correctly' );
+
+    my $credit_2 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => -10,
+            amountoutstanding => -10,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->total, 0, 'total sums correctly' );
+
+    my $credit_3 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => -100,
+            amountoutstanding => -100,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->total, -100, 'total sums correctly' );
+
+    $schema->storage->txn_rollback;
+};
+
+subtest 'credits_total() tests' => sub {
+
+    plan tests => 5;
+
+    $schema->storage->txn_begin;
+
+    my $patron  = $builder->build_object({ class => 'Koha::Patrons' });
+
+    my $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->credits_total, 0, 'credits_total returns 0 if no lines (undef case)' );
+
+    my $debit_1 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => 10,
+            amountoutstanding => 10,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    my $debit_2 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => 10,
+            amountoutstanding => 10,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->credits_total, 0, 'credits_total sums correctly' );
+
+    my $credit_1 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => -10,
+            amountoutstanding => -10,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->credits_total, -10, 'credits_total sums correctly' );
+
+    my $credit_2 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => -10,
+            amountoutstanding => -10,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->credits_total, -20, 'credits_total sums correctly' );
+
+    my $credit_3 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => -100,
+            amountoutstanding => -100,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->credits_total, -120, 'credits_total sums correctly' );
+
+    $schema->storage->txn_rollback;
+};
+
+subtest 'debits_total() tests' => sub {
+
+    plan tests => 5;
+
+    $schema->storage->txn_begin;
+
+    my $patron  = $builder->build_object({ class => 'Koha::Patrons' });
+
+    my $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->debits_total, 0, 'debits_total returns 0 if no lines (undef case)' );
+
+    my $debit_1 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => 10,
+            amountoutstanding => 0,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    my $debit_2 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => 10,
+            amountoutstanding => 0,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->debits_total, 20, 'debits_total sums correctly' );
+
+    my $credit_1 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => -10,
+            amountoutstanding => 0,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->debits_total, 20, 'debits_total sums correctly' );
+
+    my $credit_2 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => -10,
+            amountoutstanding => 0,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->debits_total, 20, 'debits_total sums correctly' );
+
+    my $credit_3 = Koha::Account::Line->new(
+        {   borrowernumber    => $patron->id,
+            debit_type_code   => "OVERDUE",
+            status            => "RETURNED",
+            amount            => -100,
+            amountoutstanding => 0,
+            interface         => 'commandline',
+        }
+    )->store;
+
+    $lines = Koha::Account::Lines->search({ borrowernumber => $patron->id });
+    is( $lines->debits_total, 20, 'debits_total sums correctly' );
+
+    $schema->storage->txn_rollback;
+};
+
 subtest 'is_credit() and is_debit() tests' => sub {
 
     plan tests => 4;
-- 
2.20.1